Embed thrift

This commit is contained in:
Sruthi Chandran 2019-03-14 13:35:24 +05:30
parent d822bc8110
commit 1730a38f1c
129 changed files with 16550 additions and 0 deletions

View file

@ -0,0 +1,43 @@
Thrift Ruby Software Library
http://thrift.apache.org
== LICENSE:
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.
== DESCRIPTION:
Thrift is a strongly-typed language-agnostic RPC system.
This library is the ruby implementation for both clients and servers.
== INSTALL:
$ gem install thrift
== CAVEATS:
This library provides the client and server implementations of thrift.
It does <em>not</em> provide the compiler for the .thrift files. To compile
.thrift files into language-specific implementations, please download the full
thrift software package.
== USAGE:
This section should get written by someone with the time and inclination.
In the meantime, look at existing code, such as the benchmark or the tutorial
in the full thrift distribution.

View file

@ -0,0 +1,24 @@
#
# 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.
#
namespace rb ThriftBenchmark
service BenchmarkService {
i32 fibonacci(1:byte n)
}

View file

@ -0,0 +1,271 @@
#
# 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.
#
require 'rubygems'
$:.unshift File.dirname(__FILE__) + '/../lib'
require 'thrift'
require 'stringio'
HOST = '127.0.0.1'
PORT = 42587
###############
## Server
###############
class Server
attr_accessor :serverclass
attr_accessor :interpreter
attr_accessor :host
attr_accessor :port
def initialize(opts)
@serverclass = opts.fetch(:class, Thrift::NonblockingServer)
@interpreter = opts.fetch(:interpreter, "ruby")
@host = opts.fetch(:host, ::HOST)
@port = opts.fetch(:port, ::PORT)
end
def start
return if @serverclass == Object
args = (File.basename(@interpreter) == "jruby" ? "-J-server" : "")
@pipe = IO.popen("#{@interpreter} #{args} #{File.dirname(__FILE__)}/server.rb #{@host} #{@port} #{@serverclass.name}", "r+")
Marshal.load(@pipe) # wait until the server has started
sleep 0.4 # give the server time to actually start spawning sockets
end
def shutdown
return unless @pipe
Marshal.dump(:shutdown, @pipe)
begin
@pipe.read(10) # block until the server shuts down
rescue EOFError
end
@pipe.close
@pipe = nil
end
end
class BenchmarkManager
def initialize(opts, server)
@socket = opts.fetch(:socket) do
@host = opts.fetch(:host, 'localhost')
@port = opts.fetch(:port)
nil
end
@num_processes = opts.fetch(:num_processes, 40)
@clients_per_process = opts.fetch(:clients_per_process, 10)
@calls_per_client = opts.fetch(:calls_per_client, 50)
@interpreter = opts.fetch(:interpreter, "ruby")
@server = server
@log_exceptions = opts.fetch(:log_exceptions, false)
end
def run
@pool = []
@benchmark_start = Time.now
puts "Spawning benchmark processes..."
@num_processes.times do
spawn
sleep 0.02 # space out spawns
end
collect_output
@benchmark_end = Time.now # we know the procs are done here
translate_output
analyze_output
report_output
end
def spawn
pipe = IO.popen("#{@interpreter} #{File.dirname(__FILE__)}/client.rb #{"-log-exceptions" if @log_exceptions} #{@host} #{@port} #{@clients_per_process} #{@calls_per_client}")
@pool << pipe
end
def socket_class
if @socket
Thrift::UNIXSocket
else
Thrift::Socket
end
end
def collect_output
puts "Collecting output..."
# read from @pool until all sockets are closed
@buffers = Hash.new { |h,k| h[k] = '' }
until @pool.empty?
rd, = select(@pool)
next if rd.nil?
rd.each do |fd|
begin
@buffers[fd] << fd.readpartial(4096)
rescue EOFError
@pool.delete fd
end
end
end
end
def translate_output
puts "Translating output..."
@output = []
@buffers.each do |fd, buffer|
strio = StringIO.new(buffer)
logs = []
begin
loop do
logs << Marshal.load(strio)
end
rescue EOFError
@output << logs
end
end
end
def analyze_output
puts "Analyzing output..."
call_times = []
client_times = []
connection_failures = []
connection_errors = []
shortest_call = 0
shortest_client = 0
longest_call = 0
longest_client = 0
@output.each do |logs|
cur_call, cur_client = nil
logs.each do |tok, time|
case tok
when :start
cur_client = time
when :call_start
cur_call = time
when :call_end
delta = time - cur_call
call_times << delta
longest_call = delta unless longest_call > delta
shortest_call = delta if shortest_call == 0 or delta < shortest_call
cur_call = nil
when :end
delta = time - cur_client
client_times << delta
longest_client = delta unless longest_client > delta
shortest_client = delta if shortest_client == 0 or delta < shortest_client
cur_client = nil
when :connection_failure
connection_failures << time
when :connection_error
connection_errors << time
end
end
end
@report = {}
@report[:total_calls] = call_times.inject(0.0) { |a,t| a += t }
@report[:avg_calls] = @report[:total_calls] / call_times.size
@report[:total_clients] = client_times.inject(0.0) { |a,t| a += t }
@report[:avg_clients] = @report[:total_clients] / client_times.size
@report[:connection_failures] = connection_failures.size
@report[:connection_errors] = connection_errors.size
@report[:shortest_call] = shortest_call
@report[:shortest_client] = shortest_client
@report[:longest_call] = longest_call
@report[:longest_client] = longest_client
@report[:total_benchmark_time] = @benchmark_end - @benchmark_start
@report[:fastthread] = $".include?('fastthread.bundle')
end
def report_output
fmt = "%.4f seconds"
puts
tabulate "%d",
[["Server class", "%s"], @server.serverclass == Object ? "" : @server.serverclass],
[["Server interpreter", "%s"], @server.interpreter],
[["Client interpreter", "%s"], @interpreter],
[["Socket class", "%s"], socket_class],
["Number of processes", @num_processes],
["Clients per process", @clients_per_process],
["Calls per client", @calls_per_client],
[["Using fastthread", "%s"], @report[:fastthread] ? "yes" : "no"]
puts
failures = (@report[:connection_failures] > 0)
tabulate fmt,
[["Connection failures", "%d", [:red, :bold]], @report[:connection_failures]],
[["Connection errors", "%d", [:red, :bold]], @report[:connection_errors]],
["Average time per call", @report[:avg_calls]],
["Average time per client (%d calls)" % @calls_per_client, @report[:avg_clients]],
["Total time for all calls", @report[:total_calls]],
["Real time for benchmarking", @report[:total_benchmark_time]],
["Shortest call time", @report[:shortest_call]],
["Longest call time", @report[:longest_call]],
["Shortest client time (%d calls)" % @calls_per_client, @report[:shortest_client]],
["Longest client time (%d calls)" % @calls_per_client, @report[:longest_client]]
end
ANSI = {
:reset => 0,
:bold => 1,
:black => 30,
:red => 31,
:green => 32,
:yellow => 33,
:blue => 34,
:magenta => 35,
:cyan => 36,
:white => 37
}
def tabulate(fmt, *labels_and_values)
labels = labels_and_values.map { |l| Array === l ? l.first : l }
label_width = labels.inject(0) { |w,l| l.size > w ? l.size : w }
labels_and_values.each do |(l,v)|
f = fmt
l, f, c = l if Array === l
fmtstr = "%-#{label_width+1}s #{f}"
if STDOUT.tty? and c and v.to_i > 0
fmtstr = "\e[#{[*c].map { |x| ANSI[x] } * ";"}m" + fmtstr + "\e[#{ANSI[:reset]}m"
end
puts fmtstr % [l+":", v]
end
end
end
def resolve_const(const)
const and const.split('::').inject(Object) { |k,c| k.const_get(c) }
end
puts "Starting server..."
args = {}
args[:interpreter] = ENV['THRIFT_SERVER_INTERPRETER'] || ENV['THRIFT_INTERPRETER'] || "ruby"
args[:class] = resolve_const(ENV['THRIFT_SERVER']) || Thrift::NonblockingServer
args[:host] = ENV['THRIFT_HOST'] || HOST
args[:port] = (ENV['THRIFT_PORT'] || PORT).to_i
server = Server.new(args)
server.start
args = {}
args[:host] = ENV['THRIFT_HOST'] || HOST
args[:port] = (ENV['THRIFT_PORT'] || PORT).to_i
args[:num_processes] = (ENV['THRIFT_NUM_PROCESSES'] || 40).to_i
args[:clients_per_process] = (ENV['THRIFT_NUM_CLIENTS'] || 5).to_i
args[:calls_per_client] = (ENV['THRIFT_NUM_CALLS'] || 50).to_i
args[:interpreter] = ENV['THRIFT_CLIENT_INTERPRETER'] || ENV['THRIFT_INTERPRETER'] || "ruby"
args[:log_exceptions] = !!ENV['THRIFT_LOG_EXCEPTIONS']
BenchmarkManager.new(args, server).run
server.shutdown

View file

@ -0,0 +1,74 @@
#
# 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.
#
$:.unshift File.dirname(__FILE__) + '/../lib'
require 'thrift'
$:.unshift File.dirname(__FILE__) + "/gen-rb"
require 'benchmark_service'
class Client
def initialize(host, port, clients_per_process, calls_per_client, log_exceptions)
@host = host
@port = port
@clients_per_process = clients_per_process
@calls_per_client = calls_per_client
@log_exceptions = log_exceptions
end
def run
@clients_per_process.times do
socket = Thrift::Socket.new(@host, @port)
transport = Thrift::FramedTransport.new(socket)
protocol = Thrift::BinaryProtocol.new(transport)
client = ThriftBenchmark::BenchmarkService::Client.new(protocol)
begin
start = Time.now
transport.open
Marshal.dump [:start, start], STDOUT
rescue => e
Marshal.dump [:connection_failure, Time.now], STDOUT
print_exception e if @log_exceptions
else
begin
@calls_per_client.times do
Marshal.dump [:call_start, Time.now], STDOUT
client.fibonacci(15)
Marshal.dump [:call_end, Time.now], STDOUT
end
transport.close
Marshal.dump [:end, Time.now], STDOUT
rescue Thrift::TransportException => e
Marshal.dump [:connection_error, Time.now], STDOUT
print_exception e if @log_exceptions
end
end
end
end
def print_exception(e)
STDERR.puts "ERROR: #{e.message}"
STDERR.puts "\t#{e.backtrace * "\n\t"}"
end
end
log_exceptions = true if ARGV[0] == '-log-exceptions' and ARGV.shift
host, port, clients_per_process, calls_per_client = ARGV
Client.new(host, port.to_i, clients_per_process.to_i, calls_per_client.to_i, log_exceptions).run

View file

@ -0,0 +1,11 @@
#
# Autogenerated by Thrift Compiler (0.11.0)
#
# DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
#
require 'thrift'
require 'benchmark_types'
module ThriftBenchmark
end

View file

@ -0,0 +1,80 @@
#
# Autogenerated by Thrift Compiler (0.11.0)
#
# DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
#
require 'thrift'
require 'benchmark_types'
module ThriftBenchmark
module BenchmarkService
class Client
include ::Thrift::Client
def fibonacci(n)
send_fibonacci(n)
return recv_fibonacci()
end
def send_fibonacci(n)
send_message('fibonacci', Fibonacci_args, :n => n)
end
def recv_fibonacci()
result = receive_message(Fibonacci_result)
return result.success unless result.success.nil?
raise ::Thrift::ApplicationException.new(::Thrift::ApplicationException::MISSING_RESULT, 'fibonacci failed: unknown result')
end
end
class Processor
include ::Thrift::Processor
def process_fibonacci(seqid, iprot, oprot)
args = read_args(iprot, Fibonacci_args)
result = Fibonacci_result.new()
result.success = @handler.fibonacci(args.n)
write_result(result, oprot, 'fibonacci', seqid)
end
end
# HELPER FUNCTIONS AND STRUCTURES
class Fibonacci_args
include ::Thrift::Struct, ::Thrift::Struct_Union
N = 1
FIELDS = {
N => {:type => ::Thrift::Types::BYTE, :name => 'n'}
}
def struct_fields; FIELDS; end
def validate
end
::Thrift::Struct.generate_accessors self
end
class Fibonacci_result
include ::Thrift::Struct, ::Thrift::Struct_Union
SUCCESS = 0
FIELDS = {
SUCCESS => {:type => ::Thrift::Types::I32, :name => 'success'}
}
def struct_fields; FIELDS; end
def validate
end
::Thrift::Struct.generate_accessors self
end
end
end

View file

@ -0,0 +1,10 @@
#
# Autogenerated by Thrift Compiler (0.11.0)
#
# DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
#
require 'thrift'
module ThriftBenchmark
end

View file

@ -0,0 +1,82 @@
#
# 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.
#
$:.unshift File.dirname(__FILE__) + '/../lib'
require 'thrift'
$:.unshift File.dirname(__FILE__) + "/gen-rb"
require 'benchmark_service'
module Server
include Thrift
class BenchmarkHandler
# 1-based index into the fibonacci sequence
def fibonacci(n)
seq = [1, 1]
3.upto(n) do
seq << seq[-1] + seq[-2]
end
seq[n-1] # n is 1-based
end
end
def self.start_server(host, port, serverClass)
handler = BenchmarkHandler.new
processor = ThriftBenchmark::BenchmarkService::Processor.new(handler)
transport = ServerSocket.new(host, port)
transport_factory = FramedTransportFactory.new
args = [processor, transport, transport_factory, nil, 20]
if serverClass == NonblockingServer
logger = Logger.new(STDERR)
logger.level = Logger::WARN
args << logger
end
server = serverClass.new(*args)
@server_thread = Thread.new do
server.serve
end
@server = server
end
def self.shutdown
return if @server.nil?
if @server.respond_to? :shutdown
@server.shutdown
else
@server_thread.kill
end
end
end
def resolve_const(const)
const and const.split('::').inject(Object) { |k,c| k.const_get(c) }
end
host, port, serverklass = ARGV
Server.start_server(host, port.to_i, resolve_const(serverklass))
# let our host know that the interpreter has started
# ideally we'd wait until the server was serving, but we don't have a hook for that
Marshal.dump(:started, STDOUT)
STDOUT.flush
Marshal.load(STDIN) # wait until we're instructed to shut down
Server.shutdown

View file

@ -0,0 +1,44 @@
#
# 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.
#
$:.unshift File.dirname(__FILE__) + '/../lib'
require 'thrift'
$:.unshift File.dirname(__FILE__) + "/gen-rb"
require 'benchmark_service'
HOST = 'localhost'
PORT = 42587
class BenchmarkHandler
# 1-based index into the fibonacci sequence
def fibonacci(n)
seq = [1, 1]
3.upto(n) do
seq << seq[-1] + seq[-2]
end
seq[n-1] # n is 1-based
end
end
handler = BenchmarkHandler.new
processor = ThriftBenchmark::BenchmarkService::Processor.new(handler)
transport = Thrift::ServerSocket.new(HOST, PORT)
transport_factory = Thrift::FramedTransportFactory.new
logger = Logger.new(STDERR)
logger.level = Logger::WARN
Thrift::NonblockingServer.new(processor, transport, transport_factory, nil, 20, logger).serve

View file

@ -0,0 +1,460 @@
/**
* 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.
*/
#include <ruby.h>
#include <stdbool.h>
#include <stdint.h>
#include <constants.h>
#include <struct.h>
#include <macros.h>
#include <bytes.h>
VALUE rb_thrift_binary_proto_native_qmark(VALUE self) {
return Qtrue;
}
static int VERSION_1;
static int VERSION_MASK;
static int TYPE_MASK;
static int BAD_VERSION;
static ID rbuf_ivar_id;
static void write_byte_direct(VALUE trans, int8_t b) {
WRITE(trans, (char*)&b, 1);
}
static void write_i16_direct(VALUE trans, int16_t value) {
char data[2];
data[1] = value;
data[0] = (value >> 8);
WRITE(trans, data, 2);
}
static void write_i32_direct(VALUE trans, int32_t value) {
char data[4];
data[3] = value;
data[2] = (value >> 8);
data[1] = (value >> 16);
data[0] = (value >> 24);
WRITE(trans, data, 4);
}
static void write_i64_direct(VALUE trans, int64_t value) {
char data[8];
data[7] = value;
data[6] = (value >> 8);
data[5] = (value >> 16);
data[4] = (value >> 24);
data[3] = (value >> 32);
data[2] = (value >> 40);
data[1] = (value >> 48);
data[0] = (value >> 56);
WRITE(trans, data, 8);
}
static void write_string_direct(VALUE trans, VALUE str) {
if (TYPE(str) != T_STRING) {
rb_raise(rb_eStandardError, "Value should be a string");
}
str = convert_to_utf8_byte_buffer(str);
write_i32_direct(trans, RSTRING_LEN(str));
rb_funcall(trans, write_method_id, 1, str);
}
//--------------------------------
// interface writing methods
//--------------------------------
VALUE rb_thrift_binary_proto_write_message_end(VALUE self) {
return Qnil;
}
VALUE rb_thrift_binary_proto_write_struct_begin(VALUE self, VALUE name) {
return Qnil;
}
VALUE rb_thrift_binary_proto_write_struct_end(VALUE self) {
return Qnil;
}
VALUE rb_thrift_binary_proto_write_field_end(VALUE self) {
return Qnil;
}
VALUE rb_thrift_binary_proto_write_map_end(VALUE self) {
return Qnil;
}
VALUE rb_thrift_binary_proto_write_list_end(VALUE self) {
return Qnil;
}
VALUE rb_thrift_binary_proto_write_set_end(VALUE self) {
return Qnil;
}
VALUE rb_thrift_binary_proto_write_message_begin(VALUE self, VALUE name, VALUE type, VALUE seqid) {
VALUE trans = GET_TRANSPORT(self);
VALUE strict_write = GET_STRICT_WRITE(self);
if (strict_write == Qtrue) {
write_i32_direct(trans, VERSION_1 | FIX2INT(type));
write_string_direct(trans, name);
write_i32_direct(trans, FIX2INT(seqid));
} else {
write_string_direct(trans, name);
write_byte_direct(trans, FIX2INT(type));
write_i32_direct(trans, FIX2INT(seqid));
}
return Qnil;
}
VALUE rb_thrift_binary_proto_write_field_begin(VALUE self, VALUE name, VALUE type, VALUE id) {
VALUE trans = GET_TRANSPORT(self);
write_byte_direct(trans, FIX2INT(type));
write_i16_direct(trans, FIX2INT(id));
return Qnil;
}
VALUE rb_thrift_binary_proto_write_field_stop(VALUE self) {
write_byte_direct(GET_TRANSPORT(self), TTYPE_STOP);
return Qnil;
}
VALUE rb_thrift_binary_proto_write_map_begin(VALUE self, VALUE ktype, VALUE vtype, VALUE size) {
VALUE trans = GET_TRANSPORT(self);
write_byte_direct(trans, FIX2INT(ktype));
write_byte_direct(trans, FIX2INT(vtype));
write_i32_direct(trans, FIX2INT(size));
return Qnil;
}
VALUE rb_thrift_binary_proto_write_list_begin(VALUE self, VALUE etype, VALUE size) {
VALUE trans = GET_TRANSPORT(self);
write_byte_direct(trans, FIX2INT(etype));
write_i32_direct(trans, FIX2INT(size));
return Qnil;
}
VALUE rb_thrift_binary_proto_write_set_begin(VALUE self, VALUE etype, VALUE size) {
rb_thrift_binary_proto_write_list_begin(self, etype, size);
return Qnil;
}
VALUE rb_thrift_binary_proto_write_bool(VALUE self, VALUE b) {
write_byte_direct(GET_TRANSPORT(self), RTEST(b) ? 1 : 0);
return Qnil;
}
VALUE rb_thrift_binary_proto_write_byte(VALUE self, VALUE byte) {
CHECK_NIL(byte);
write_byte_direct(GET_TRANSPORT(self), NUM2INT(byte));
return Qnil;
}
VALUE rb_thrift_binary_proto_write_i16(VALUE self, VALUE i16) {
CHECK_NIL(i16);
write_i16_direct(GET_TRANSPORT(self), FIX2INT(i16));
return Qnil;
}
VALUE rb_thrift_binary_proto_write_i32(VALUE self, VALUE i32) {
CHECK_NIL(i32);
write_i32_direct(GET_TRANSPORT(self), NUM2INT(i32));
return Qnil;
}
VALUE rb_thrift_binary_proto_write_i64(VALUE self, VALUE i64) {
CHECK_NIL(i64);
write_i64_direct(GET_TRANSPORT(self), NUM2LL(i64));
return Qnil;
}
VALUE rb_thrift_binary_proto_write_double(VALUE self, VALUE dub) {
CHECK_NIL(dub);
// Unfortunately, bitwise_cast doesn't work in C. Bad C!
union {
double f;
int64_t t;
} transfer;
transfer.f = RFLOAT_VALUE(rb_Float(dub));
write_i64_direct(GET_TRANSPORT(self), transfer.t);
return Qnil;
}
VALUE rb_thrift_binary_proto_write_string(VALUE self, VALUE str) {
CHECK_NIL(str);
VALUE trans = GET_TRANSPORT(self);
write_string_direct(trans, str);
return Qnil;
}
VALUE rb_thrift_binary_proto_write_binary(VALUE self, VALUE buf) {
CHECK_NIL(buf);
VALUE trans = GET_TRANSPORT(self);
buf = force_binary_encoding(buf);
write_i32_direct(trans, RSTRING_LEN(buf));
rb_funcall(trans, write_method_id, 1, buf);
return Qnil;
}
//---------------------------------------
// interface reading methods
//---------------------------------------
VALUE rb_thrift_binary_proto_read_string(VALUE self);
VALUE rb_thrift_binary_proto_read_binary(VALUE self);
VALUE rb_thrift_binary_proto_read_byte(VALUE self);
VALUE rb_thrift_binary_proto_read_i32(VALUE self);
VALUE rb_thrift_binary_proto_read_i16(VALUE self);
static char read_byte_direct(VALUE self) {
VALUE byte = rb_funcall(GET_TRANSPORT(self), read_byte_method_id, 0);
return (char)(FIX2INT(byte));
}
static int16_t read_i16_direct(VALUE self) {
VALUE rbuf = rb_ivar_get(self, rbuf_ivar_id);
rb_funcall(GET_TRANSPORT(self), read_into_buffer_method_id, 2, rbuf, INT2FIX(2));
return (int16_t)(((uint8_t)(RSTRING_PTR(rbuf)[1])) | ((uint16_t)((RSTRING_PTR(rbuf)[0]) << 8)));
}
static int32_t read_i32_direct(VALUE self) {
VALUE rbuf = rb_ivar_get(self, rbuf_ivar_id);
rb_funcall(GET_TRANSPORT(self), read_into_buffer_method_id, 2, rbuf, INT2FIX(4));
return ((uint8_t)(RSTRING_PTR(rbuf)[3])) |
(((uint8_t)(RSTRING_PTR(rbuf)[2])) << 8) |
(((uint8_t)(RSTRING_PTR(rbuf)[1])) << 16) |
(((uint8_t)(RSTRING_PTR(rbuf)[0])) << 24);
}
static int64_t read_i64_direct(VALUE self) {
VALUE rbuf = rb_ivar_get(self, rbuf_ivar_id);
rb_funcall(GET_TRANSPORT(self), read_into_buffer_method_id, 2, rbuf, INT2FIX(8));
uint64_t hi = ((uint8_t)(RSTRING_PTR(rbuf)[3])) |
(((uint8_t)(RSTRING_PTR(rbuf)[2])) << 8) |
(((uint8_t)(RSTRING_PTR(rbuf)[1])) << 16) |
(((uint8_t)(RSTRING_PTR(rbuf)[0])) << 24);
uint32_t lo = ((uint8_t)(RSTRING_PTR(rbuf)[7])) |
(((uint8_t)(RSTRING_PTR(rbuf)[6])) << 8) |
(((uint8_t)(RSTRING_PTR(rbuf)[5])) << 16) |
(((uint8_t)(RSTRING_PTR(rbuf)[4])) << 24);
return (hi << 32) | lo;
}
static VALUE get_protocol_exception(VALUE code, VALUE message) {
VALUE args[2];
args[0] = code;
args[1] = message;
return rb_class_new_instance(2, (VALUE*)&args, protocol_exception_class);
}
VALUE rb_thrift_binary_proto_read_message_end(VALUE self) {
return Qnil;
}
VALUE rb_thrift_binary_proto_read_struct_begin(VALUE self) {
return Qnil;
}
VALUE rb_thrift_binary_proto_read_struct_end(VALUE self) {
return Qnil;
}
VALUE rb_thrift_binary_proto_read_field_end(VALUE self) {
return Qnil;
}
VALUE rb_thrift_binary_proto_read_map_end(VALUE self) {
return Qnil;
}
VALUE rb_thrift_binary_proto_read_list_end(VALUE self) {
return Qnil;
}
VALUE rb_thrift_binary_proto_read_set_end(VALUE self) {
return Qnil;
}
VALUE rb_thrift_binary_proto_read_message_begin(VALUE self) {
VALUE strict_read = GET_STRICT_READ(self);
VALUE name, seqid;
int type;
int version = read_i32_direct(self);
if (version < 0) {
if ((version & VERSION_MASK) != VERSION_1) {
rb_exc_raise(get_protocol_exception(INT2FIX(BAD_VERSION), rb_str_new2("Missing version identifier")));
}
type = version & TYPE_MASK;
name = rb_thrift_binary_proto_read_string(self);
seqid = rb_thrift_binary_proto_read_i32(self);
} else {
if (strict_read == Qtrue) {
rb_exc_raise(get_protocol_exception(INT2FIX(BAD_VERSION), rb_str_new2("No version identifier, old protocol client?")));
}
name = READ(self, version);
type = read_byte_direct(self);
seqid = rb_thrift_binary_proto_read_i32(self);
}
return rb_ary_new3(3, name, INT2FIX(type), seqid);
}
VALUE rb_thrift_binary_proto_read_field_begin(VALUE self) {
int type = read_byte_direct(self);
if (type == TTYPE_STOP) {
return rb_ary_new3(3, Qnil, INT2FIX(type), INT2FIX(0));
} else {
VALUE id = rb_thrift_binary_proto_read_i16(self);
return rb_ary_new3(3, Qnil, INT2FIX(type), id);
}
}
VALUE rb_thrift_binary_proto_read_map_begin(VALUE self) {
VALUE ktype = rb_thrift_binary_proto_read_byte(self);
VALUE vtype = rb_thrift_binary_proto_read_byte(self);
VALUE size = rb_thrift_binary_proto_read_i32(self);
return rb_ary_new3(3, ktype, vtype, size);
}
VALUE rb_thrift_binary_proto_read_list_begin(VALUE self) {
VALUE etype = rb_thrift_binary_proto_read_byte(self);
VALUE size = rb_thrift_binary_proto_read_i32(self);
return rb_ary_new3(2, etype, size);
}
VALUE rb_thrift_binary_proto_read_set_begin(VALUE self) {
return rb_thrift_binary_proto_read_list_begin(self);
}
VALUE rb_thrift_binary_proto_read_bool(VALUE self) {
char byte = read_byte_direct(self);
return byte != 0 ? Qtrue : Qfalse;
}
VALUE rb_thrift_binary_proto_read_byte(VALUE self) {
return INT2FIX(read_byte_direct(self));
}
VALUE rb_thrift_binary_proto_read_i16(VALUE self) {
return INT2FIX(read_i16_direct(self));
}
VALUE rb_thrift_binary_proto_read_i32(VALUE self) {
return INT2NUM(read_i32_direct(self));
}
VALUE rb_thrift_binary_proto_read_i64(VALUE self) {
return LL2NUM(read_i64_direct(self));
}
VALUE rb_thrift_binary_proto_read_double(VALUE self) {
union {
double f;
int64_t t;
} transfer;
transfer.t = read_i64_direct(self);
return rb_float_new(transfer.f);
}
VALUE rb_thrift_binary_proto_read_string(VALUE self) {
VALUE buffer = rb_thrift_binary_proto_read_binary(self);
return convert_to_string(buffer);
}
VALUE rb_thrift_binary_proto_read_binary(VALUE self) {
int size = read_i32_direct(self);
return READ(self, size);
}
void Init_binary_protocol_accelerated() {
VALUE thrift_binary_protocol_class = rb_const_get(thrift_module, rb_intern("BinaryProtocol"));
VERSION_1 = rb_num2ll(rb_const_get(thrift_binary_protocol_class, rb_intern("VERSION_1")));
VERSION_MASK = rb_num2ll(rb_const_get(thrift_binary_protocol_class, rb_intern("VERSION_MASK")));
TYPE_MASK = rb_num2ll(rb_const_get(thrift_binary_protocol_class, rb_intern("TYPE_MASK")));
VALUE bpa_class = rb_define_class_under(thrift_module, "BinaryProtocolAccelerated", thrift_binary_protocol_class);
rb_define_method(bpa_class, "native?", rb_thrift_binary_proto_native_qmark, 0);
rb_define_method(bpa_class, "write_message_begin", rb_thrift_binary_proto_write_message_begin, 3);
rb_define_method(bpa_class, "write_field_begin", rb_thrift_binary_proto_write_field_begin, 3);
rb_define_method(bpa_class, "write_field_stop", rb_thrift_binary_proto_write_field_stop, 0);
rb_define_method(bpa_class, "write_map_begin", rb_thrift_binary_proto_write_map_begin, 3);
rb_define_method(bpa_class, "write_list_begin", rb_thrift_binary_proto_write_list_begin, 2);
rb_define_method(bpa_class, "write_set_begin", rb_thrift_binary_proto_write_set_begin, 2);
rb_define_method(bpa_class, "write_byte", rb_thrift_binary_proto_write_byte, 1);
rb_define_method(bpa_class, "write_bool", rb_thrift_binary_proto_write_bool, 1);
rb_define_method(bpa_class, "write_i16", rb_thrift_binary_proto_write_i16, 1);
rb_define_method(bpa_class, "write_i32", rb_thrift_binary_proto_write_i32, 1);
rb_define_method(bpa_class, "write_i64", rb_thrift_binary_proto_write_i64, 1);
rb_define_method(bpa_class, "write_double", rb_thrift_binary_proto_write_double, 1);
rb_define_method(bpa_class, "write_string", rb_thrift_binary_proto_write_string, 1);
rb_define_method(bpa_class, "write_binary", rb_thrift_binary_proto_write_binary, 1);
// unused methods
rb_define_method(bpa_class, "write_message_end", rb_thrift_binary_proto_write_message_end, 0);
rb_define_method(bpa_class, "write_struct_begin", rb_thrift_binary_proto_write_struct_begin, 1);
rb_define_method(bpa_class, "write_struct_end", rb_thrift_binary_proto_write_struct_end, 0);
rb_define_method(bpa_class, "write_field_end", rb_thrift_binary_proto_write_field_end, 0);
rb_define_method(bpa_class, "write_map_end", rb_thrift_binary_proto_write_map_end, 0);
rb_define_method(bpa_class, "write_list_end", rb_thrift_binary_proto_write_list_end, 0);
rb_define_method(bpa_class, "write_set_end", rb_thrift_binary_proto_write_set_end, 0);
rb_define_method(bpa_class, "read_message_begin", rb_thrift_binary_proto_read_message_begin, 0);
rb_define_method(bpa_class, "read_field_begin", rb_thrift_binary_proto_read_field_begin, 0);
rb_define_method(bpa_class, "read_map_begin", rb_thrift_binary_proto_read_map_begin, 0);
rb_define_method(bpa_class, "read_list_begin", rb_thrift_binary_proto_read_list_begin, 0);
rb_define_method(bpa_class, "read_set_begin", rb_thrift_binary_proto_read_set_begin, 0);
rb_define_method(bpa_class, "read_byte", rb_thrift_binary_proto_read_byte, 0);
rb_define_method(bpa_class, "read_bool", rb_thrift_binary_proto_read_bool, 0);
rb_define_method(bpa_class, "read_i16", rb_thrift_binary_proto_read_i16, 0);
rb_define_method(bpa_class, "read_i32", rb_thrift_binary_proto_read_i32, 0);
rb_define_method(bpa_class, "read_i64", rb_thrift_binary_proto_read_i64, 0);
rb_define_method(bpa_class, "read_double", rb_thrift_binary_proto_read_double, 0);
rb_define_method(bpa_class, "read_string", rb_thrift_binary_proto_read_string, 0);
rb_define_method(bpa_class, "read_binary", rb_thrift_binary_proto_read_binary, 0);
// unused methods
rb_define_method(bpa_class, "read_message_end", rb_thrift_binary_proto_read_message_end, 0);
rb_define_method(bpa_class, "read_struct_begin", rb_thrift_binary_proto_read_struct_begin, 0);
rb_define_method(bpa_class, "read_struct_end", rb_thrift_binary_proto_read_struct_end, 0);
rb_define_method(bpa_class, "read_field_end", rb_thrift_binary_proto_read_field_end, 0);
rb_define_method(bpa_class, "read_map_end", rb_thrift_binary_proto_read_map_end, 0);
rb_define_method(bpa_class, "read_list_end", rb_thrift_binary_proto_read_list_end, 0);
rb_define_method(bpa_class, "read_set_end", rb_thrift_binary_proto_read_set_end, 0);
rbuf_ivar_id = rb_intern("@rbuf");
}

View file

@ -0,0 +1,20 @@
/*
* 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.
*/
void Init_binary_protocol_accelerated();

View file

@ -0,0 +1,36 @@
/**
* 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.
*/
#include <ruby.h>
#ifdef HAVE_RUBY_ENCODING_H
#include <ruby/encoding.h>
#endif
#include <constants.h>
VALUE force_binary_encoding(VALUE buffer) {
return rb_funcall(thrift_bytes_module, force_binary_encoding_id, 1, buffer);
}
VALUE convert_to_utf8_byte_buffer(VALUE string) {
return rb_funcall(thrift_bytes_module, convert_to_utf8_byte_buffer_id, 1, string);
}
VALUE convert_to_string(VALUE utf8_buffer) {
return rb_funcall(thrift_bytes_module, convert_to_string_id, 1, utf8_buffer);
}

View file

@ -0,0 +1,31 @@
/**
* 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.
*/
#include <ruby.h>
/*
* A collection of utilities for working with bytes and byte buffers.
*
* These methods are the native analogies to some of the methods in
* Thrift::Bytes (thrift/bytes.rb).
*/
VALUE force_binary_encoding(VALUE buffer);
VALUE convert_to_utf8_byte_buffer(VALUE string);
VALUE convert_to_string(VALUE utf8_buffer);

View file

@ -0,0 +1,637 @@
/**
* 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.
*/
#include <ruby.h>
#include <stdbool.h>
#include <stdint.h>
#include <constants.h>
#include <struct.h>
#include <macros.h>
#include <bytes.h>
#define LAST_ID(obj) FIX2INT(rb_ary_pop(rb_ivar_get(obj, last_field_id)))
#define SET_LAST_ID(obj, val) rb_ary_push(rb_ivar_get(obj, last_field_id), val)
VALUE rb_thrift_compact_proto_native_qmark(VALUE self) {
return Qtrue;
}
static ID last_field_id;
static ID boolean_field_id;
static ID bool_value_id;
static ID rbuf_ivar_id;
static int VERSION;
static int VERSION_MASK;
static int TYPE_MASK;
static int TYPE_BITS;
static int TYPE_SHIFT_AMOUNT;
static int PROTOCOL_ID;
static VALUE thrift_compact_protocol_class;
static int CTYPE_BOOLEAN_TRUE = 0x01;
static int CTYPE_BOOLEAN_FALSE = 0x02;
static int CTYPE_BYTE = 0x03;
static int CTYPE_I16 = 0x04;
static int CTYPE_I32 = 0x05;
static int CTYPE_I64 = 0x06;
static int CTYPE_DOUBLE = 0x07;
static int CTYPE_BINARY = 0x08;
static int CTYPE_LIST = 0x09;
static int CTYPE_SET = 0x0A;
static int CTYPE_MAP = 0x0B;
static int CTYPE_STRUCT = 0x0C;
VALUE rb_thrift_compact_proto_write_i16(VALUE self, VALUE i16);
// TODO: implement this
static int get_compact_type(VALUE type_value) {
int type = FIX2INT(type_value);
if (type == TTYPE_BOOL) {
return CTYPE_BOOLEAN_TRUE;
} else if (type == TTYPE_BYTE) {
return CTYPE_BYTE;
} else if (type == TTYPE_I16) {
return CTYPE_I16;
} else if (type == TTYPE_I32) {
return CTYPE_I32;
} else if (type == TTYPE_I64) {
return CTYPE_I64;
} else if (type == TTYPE_DOUBLE) {
return CTYPE_DOUBLE;
} else if (type == TTYPE_STRING) {
return CTYPE_BINARY;
} else if (type == TTYPE_LIST) {
return CTYPE_LIST;
} else if (type == TTYPE_SET) {
return CTYPE_SET;
} else if (type == TTYPE_MAP) {
return CTYPE_MAP;
} else if (type == TTYPE_STRUCT) {
return CTYPE_STRUCT;
} else {
char str[50];
sprintf(str, "don't know what type: %d", type);
rb_raise(rb_eStandardError, "%s", str);
return 0;
}
}
static void write_byte_direct(VALUE transport, int8_t b) {
WRITE(transport, (char*)&b, 1);
}
static void write_field_begin_internal(VALUE self, VALUE type, VALUE id_value, VALUE type_override) {
int id = FIX2INT(id_value);
int last_id = LAST_ID(self);
VALUE transport = GET_TRANSPORT(self);
// if there's a type override, use that.
int8_t type_to_write = RTEST(type_override) ? FIX2INT(type_override) : get_compact_type(type);
// check if we can use delta encoding for the field id
int diff = id - last_id;
if (diff > 0 && diff <= 15) {
// write them together
write_byte_direct(transport, diff << 4 | (type_to_write & 0x0f));
} else {
// write them separate
write_byte_direct(transport, type_to_write & 0x0f);
rb_thrift_compact_proto_write_i16(self, id_value);
}
SET_LAST_ID(self, id_value);
}
static int32_t int_to_zig_zag(int32_t n) {
return (n << 1) ^ (n >> 31);
}
static uint64_t ll_to_zig_zag(int64_t n) {
return (n << 1) ^ (n >> 63);
}
static void write_varint32(VALUE transport, uint32_t n) {
while (true) {
if ((n & ~0x7F) == 0) {
write_byte_direct(transport, n & 0x7f);
break;
} else {
write_byte_direct(transport, (n & 0x7F) | 0x80);
n = n >> 7;
}
}
}
static void write_varint64(VALUE transport, uint64_t n) {
while (true) {
if ((n & ~0x7F) == 0) {
write_byte_direct(transport, n & 0x7f);
break;
} else {
write_byte_direct(transport, (n & 0x7F) | 0x80);
n = n >> 7;
}
}
}
static void write_collection_begin(VALUE transport, VALUE elem_type, VALUE size_value) {
int size = FIX2INT(size_value);
if (size <= 14) {
write_byte_direct(transport, size << 4 | get_compact_type(elem_type));
} else {
write_byte_direct(transport, 0xf0 | get_compact_type(elem_type));
write_varint32(transport, size);
}
}
//--------------------------------
// interface writing methods
//--------------------------------
VALUE rb_thrift_compact_proto_write_i32(VALUE self, VALUE i32);
VALUE rb_thrift_compact_proto_write_string(VALUE self, VALUE str);
VALUE rb_thrift_compact_proto_write_binary(VALUE self, VALUE buf);
VALUE rb_thrift_compact_proto_write_message_end(VALUE self) {
return Qnil;
}
VALUE rb_thrift_compact_proto_write_struct_begin(VALUE self, VALUE name) {
rb_ary_push(rb_ivar_get(self, last_field_id), INT2FIX(0));
return Qnil;
}
VALUE rb_thrift_compact_proto_write_struct_end(VALUE self) {
rb_ary_pop(rb_ivar_get(self, last_field_id));
return Qnil;
}
VALUE rb_thrift_compact_proto_write_field_end(VALUE self) {
return Qnil;
}
VALUE rb_thrift_compact_proto_write_map_end(VALUE self) {
return Qnil;
}
VALUE rb_thrift_compact_proto_write_list_end(VALUE self) {
return Qnil;
}
VALUE rb_thrift_compact_proto_write_set_end(VALUE self) {
return Qnil;
}
VALUE rb_thrift_compact_proto_write_message_begin(VALUE self, VALUE name, VALUE type, VALUE seqid) {
VALUE transport = GET_TRANSPORT(self);
write_byte_direct(transport, PROTOCOL_ID);
write_byte_direct(transport, (VERSION & VERSION_MASK) | ((FIX2INT(type) << TYPE_SHIFT_AMOUNT) & TYPE_MASK));
write_varint32(transport, FIX2INT(seqid));
rb_thrift_compact_proto_write_string(self, name);
return Qnil;
}
VALUE rb_thrift_compact_proto_write_field_begin(VALUE self, VALUE name, VALUE type, VALUE id) {
if (FIX2INT(type) == TTYPE_BOOL) {
// we want to possibly include the value, so we'll wait.
rb_ivar_set(self, boolean_field_id, rb_ary_new3(2, type, id));
} else {
write_field_begin_internal(self, type, id, Qnil);
}
return Qnil;
}
VALUE rb_thrift_compact_proto_write_field_stop(VALUE self) {
write_byte_direct(GET_TRANSPORT(self), TTYPE_STOP);
return Qnil;
}
VALUE rb_thrift_compact_proto_write_map_begin(VALUE self, VALUE ktype, VALUE vtype, VALUE size_value) {
int size = FIX2INT(size_value);
VALUE transport = GET_TRANSPORT(self);
if (size == 0) {
write_byte_direct(transport, 0);
} else {
write_varint32(transport, size);
write_byte_direct(transport, get_compact_type(ktype) << 4 | get_compact_type(vtype));
}
return Qnil;
}
VALUE rb_thrift_compact_proto_write_list_begin(VALUE self, VALUE etype, VALUE size) {
write_collection_begin(GET_TRANSPORT(self), etype, size);
return Qnil;
}
VALUE rb_thrift_compact_proto_write_set_begin(VALUE self, VALUE etype, VALUE size) {
write_collection_begin(GET_TRANSPORT(self), etype, size);
return Qnil;
}
VALUE rb_thrift_compact_proto_write_bool(VALUE self, VALUE b) {
int8_t type = b == Qtrue ? CTYPE_BOOLEAN_TRUE : CTYPE_BOOLEAN_FALSE;
VALUE boolean_field = rb_ivar_get(self, boolean_field_id);
if (NIL_P(boolean_field)) {
// we're not part of a field, so just write the value.
write_byte_direct(GET_TRANSPORT(self), type);
} else {
// we haven't written the field header yet
write_field_begin_internal(self, rb_ary_entry(boolean_field, 0), rb_ary_entry(boolean_field, 1), INT2FIX(type));
rb_ivar_set(self, boolean_field_id, Qnil);
}
return Qnil;
}
VALUE rb_thrift_compact_proto_write_byte(VALUE self, VALUE byte) {
CHECK_NIL(byte);
write_byte_direct(GET_TRANSPORT(self), FIX2INT(byte));
return Qnil;
}
VALUE rb_thrift_compact_proto_write_i16(VALUE self, VALUE i16) {
rb_thrift_compact_proto_write_i32(self, i16);
return Qnil;
}
VALUE rb_thrift_compact_proto_write_i32(VALUE self, VALUE i32) {
CHECK_NIL(i32);
write_varint32(GET_TRANSPORT(self), int_to_zig_zag(NUM2INT(i32)));
return Qnil;
}
VALUE rb_thrift_compact_proto_write_i64(VALUE self, VALUE i64) {
CHECK_NIL(i64);
write_varint64(GET_TRANSPORT(self), ll_to_zig_zag(NUM2LL(i64)));
return Qnil;
}
VALUE rb_thrift_compact_proto_write_double(VALUE self, VALUE dub) {
CHECK_NIL(dub);
// Unfortunately, bitwise_cast doesn't work in C. Bad C!
union {
double f;
int64_t l;
} transfer;
transfer.f = RFLOAT_VALUE(rb_Float(dub));
char buf[8];
buf[0] = transfer.l & 0xff;
buf[1] = (transfer.l >> 8) & 0xff;
buf[2] = (transfer.l >> 16) & 0xff;
buf[3] = (transfer.l >> 24) & 0xff;
buf[4] = (transfer.l >> 32) & 0xff;
buf[5] = (transfer.l >> 40) & 0xff;
buf[6] = (transfer.l >> 48) & 0xff;
buf[7] = (transfer.l >> 56) & 0xff;
WRITE(GET_TRANSPORT(self), buf, 8);
return Qnil;
}
VALUE rb_thrift_compact_proto_write_string(VALUE self, VALUE str) {
str = convert_to_utf8_byte_buffer(str);
rb_thrift_compact_proto_write_binary(self, str);
return Qnil;
}
VALUE rb_thrift_compact_proto_write_binary(VALUE self, VALUE buf) {
buf = force_binary_encoding(buf);
VALUE transport = GET_TRANSPORT(self);
write_varint32(transport, RSTRING_LEN(buf));
WRITE(transport, StringValuePtr(buf), RSTRING_LEN(buf));
return Qnil;
}
//---------------------------------------
// interface reading methods
//---------------------------------------
#define is_bool_type(ctype) (((ctype) & 0x0F) == CTYPE_BOOLEAN_TRUE || ((ctype) & 0x0F) == CTYPE_BOOLEAN_FALSE)
VALUE rb_thrift_compact_proto_read_string(VALUE self);
VALUE rb_thrift_compact_proto_read_binary(VALUE self);
VALUE rb_thrift_compact_proto_read_byte(VALUE self);
VALUE rb_thrift_compact_proto_read_i32(VALUE self);
VALUE rb_thrift_compact_proto_read_i16(VALUE self);
static int8_t get_ttype(int8_t ctype) {
if (ctype == TTYPE_STOP) {
return TTYPE_STOP;
} else if (ctype == CTYPE_BOOLEAN_TRUE || ctype == CTYPE_BOOLEAN_FALSE) {
return TTYPE_BOOL;
} else if (ctype == CTYPE_BYTE) {
return TTYPE_BYTE;
} else if (ctype == CTYPE_I16) {
return TTYPE_I16;
} else if (ctype == CTYPE_I32) {
return TTYPE_I32;
} else if (ctype == CTYPE_I64) {
return TTYPE_I64;
} else if (ctype == CTYPE_DOUBLE) {
return TTYPE_DOUBLE;
} else if (ctype == CTYPE_BINARY) {
return TTYPE_STRING;
} else if (ctype == CTYPE_LIST) {
return TTYPE_LIST;
} else if (ctype == CTYPE_SET) {
return TTYPE_SET;
} else if (ctype == CTYPE_MAP) {
return TTYPE_MAP;
} else if (ctype == CTYPE_STRUCT) {
return TTYPE_STRUCT;
} else {
char str[50];
sprintf(str, "don't know what type: %d", ctype);
rb_raise(rb_eStandardError, "%s", str);
return 0;
}
}
static char read_byte_direct(VALUE self) {
VALUE byte = rb_funcall(GET_TRANSPORT(self), read_byte_method_id, 0);
return (char)(FIX2INT(byte));
}
static int64_t zig_zag_to_ll(int64_t n) {
return (((uint64_t)n) >> 1) ^ -(n & 1);
}
static int32_t zig_zag_to_int(int32_t n) {
return (((uint32_t)n) >> 1) ^ -(n & 1);
}
static int64_t read_varint64(VALUE self) {
int shift = 0;
int64_t result = 0;
while (true) {
int8_t b = read_byte_direct(self);
result = result | ((uint64_t)(b & 0x7f) << shift);
if ((b & 0x80) != 0x80) {
break;
}
shift += 7;
}
return result;
}
static int16_t read_i16(VALUE self) {
return zig_zag_to_int((int32_t)read_varint64(self));
}
static VALUE get_protocol_exception(VALUE code, VALUE message) {
VALUE args[2];
args[0] = code;
args[1] = message;
return rb_class_new_instance(2, (VALUE*)&args, protocol_exception_class);
}
VALUE rb_thrift_compact_proto_read_message_end(VALUE self) {
return Qnil;
}
VALUE rb_thrift_compact_proto_read_struct_begin(VALUE self) {
rb_ary_push(rb_ivar_get(self, last_field_id), INT2FIX(0));
return Qnil;
}
VALUE rb_thrift_compact_proto_read_struct_end(VALUE self) {
rb_ary_pop(rb_ivar_get(self, last_field_id));
return Qnil;
}
VALUE rb_thrift_compact_proto_read_field_end(VALUE self) {
return Qnil;
}
VALUE rb_thrift_compact_proto_read_map_end(VALUE self) {
return Qnil;
}
VALUE rb_thrift_compact_proto_read_list_end(VALUE self) {
return Qnil;
}
VALUE rb_thrift_compact_proto_read_set_end(VALUE self) {
return Qnil;
}
VALUE rb_thrift_compact_proto_read_message_begin(VALUE self) {
int8_t protocol_id = read_byte_direct(self);
if (protocol_id != PROTOCOL_ID) {
char buf[100];
int len = sprintf(buf, "Expected protocol id %d but got %d", PROTOCOL_ID, protocol_id);
buf[len] = 0;
rb_exc_raise(get_protocol_exception(INT2FIX(-1), rb_str_new2(buf)));
}
int8_t version_and_type = read_byte_direct(self);
int8_t version = version_and_type & VERSION_MASK;
if (version != VERSION) {
char buf[100];
int len = sprintf(buf, "Expected version id %d but got %d", version, VERSION);
buf[len] = 0;
rb_exc_raise(get_protocol_exception(INT2FIX(-1), rb_str_new2(buf)));
}
int8_t type = (version_and_type >> TYPE_SHIFT_AMOUNT) & TYPE_BITS;
int32_t seqid = read_varint64(self);
VALUE messageName = rb_thrift_compact_proto_read_string(self);
return rb_ary_new3(3, messageName, INT2FIX(type), INT2NUM(seqid));
}
VALUE rb_thrift_compact_proto_read_field_begin(VALUE self) {
int8_t type = read_byte_direct(self);
// if it's a stop, then we can return immediately, as the struct is over.
if ((type & 0x0f) == TTYPE_STOP) {
return rb_ary_new3(3, Qnil, INT2FIX(0), INT2FIX(0));
} else {
int field_id = 0;
// mask off the 4 MSB of the type header. it could contain a field id delta.
uint8_t modifier = ((type & 0xf0) >> 4);
if (modifier == 0) {
// not a delta. look ahead for the zigzag varint field id.
(void) LAST_ID(self);
field_id = read_i16(self);
} else {
// has a delta. add the delta to the last read field id.
field_id = LAST_ID(self) + modifier;
}
// if this happens to be a boolean field, the value is encoded in the type
if (is_bool_type(type)) {
// save the boolean value in a special instance variable.
rb_ivar_set(self, bool_value_id, (type & 0x0f) == CTYPE_BOOLEAN_TRUE ? Qtrue : Qfalse);
}
// push the new field onto the field stack so we can keep the deltas going.
SET_LAST_ID(self, INT2FIX(field_id));
return rb_ary_new3(3, Qnil, INT2FIX(get_ttype(type & 0x0f)), INT2FIX(field_id));
}
}
VALUE rb_thrift_compact_proto_read_map_begin(VALUE self) {
int32_t size = read_varint64(self);
uint8_t key_and_value_type = size == 0 ? 0 : read_byte_direct(self);
return rb_ary_new3(3, INT2FIX(get_ttype(key_and_value_type >> 4)), INT2FIX(get_ttype(key_and_value_type & 0xf)), INT2FIX(size));
}
VALUE rb_thrift_compact_proto_read_list_begin(VALUE self) {
uint8_t size_and_type = read_byte_direct(self);
int32_t size = (size_and_type >> 4) & 0x0f;
if (size == 15) {
size = read_varint64(self);
}
uint8_t type = get_ttype(size_and_type & 0x0f);
return rb_ary_new3(2, INT2FIX(type), INT2FIX(size));
}
VALUE rb_thrift_compact_proto_read_set_begin(VALUE self) {
return rb_thrift_compact_proto_read_list_begin(self);
}
VALUE rb_thrift_compact_proto_read_bool(VALUE self) {
VALUE bool_value = rb_ivar_get(self, bool_value_id);
if (NIL_P(bool_value)) {
return read_byte_direct(self) == CTYPE_BOOLEAN_TRUE ? Qtrue : Qfalse;
} else {
rb_ivar_set(self, bool_value_id, Qnil);
return bool_value;
}
}
VALUE rb_thrift_compact_proto_read_byte(VALUE self) {
return INT2FIX(read_byte_direct(self));
}
VALUE rb_thrift_compact_proto_read_i16(VALUE self) {
return INT2FIX(read_i16(self));
}
VALUE rb_thrift_compact_proto_read_i32(VALUE self) {
return INT2NUM(zig_zag_to_int(read_varint64(self)));
}
VALUE rb_thrift_compact_proto_read_i64(VALUE self) {
return LL2NUM(zig_zag_to_ll(read_varint64(self)));
}
VALUE rb_thrift_compact_proto_read_double(VALUE self) {
union {
double f;
int64_t l;
} transfer;
VALUE rbuf = rb_ivar_get(self, rbuf_ivar_id);
rb_funcall(GET_TRANSPORT(self), read_into_buffer_method_id, 2, rbuf, INT2FIX(8));
uint32_t lo = ((uint8_t)(RSTRING_PTR(rbuf)[0]))
| (((uint8_t)(RSTRING_PTR(rbuf)[1])) << 8)
| (((uint8_t)(RSTRING_PTR(rbuf)[2])) << 16)
| (((uint8_t)(RSTRING_PTR(rbuf)[3])) << 24);
uint64_t hi = (((uint8_t)(RSTRING_PTR(rbuf)[4])))
| (((uint8_t)(RSTRING_PTR(rbuf)[5])) << 8)
| (((uint8_t)(RSTRING_PTR(rbuf)[6])) << 16)
| (((uint8_t)(RSTRING_PTR(rbuf)[7])) << 24);
transfer.l = (hi << 32) | lo;
return rb_float_new(transfer.f);
}
VALUE rb_thrift_compact_proto_read_string(VALUE self) {
VALUE buffer = rb_thrift_compact_proto_read_binary(self);
return convert_to_string(buffer);
}
VALUE rb_thrift_compact_proto_read_binary(VALUE self) {
int64_t size = read_varint64(self);
return READ(self, size);
}
static void Init_constants() {
thrift_compact_protocol_class = rb_const_get(thrift_module, rb_intern("CompactProtocol"));
VERSION = rb_num2ll(rb_const_get(thrift_compact_protocol_class, rb_intern("VERSION")));
VERSION_MASK = rb_num2ll(rb_const_get(thrift_compact_protocol_class, rb_intern("VERSION_MASK")));
TYPE_MASK = rb_num2ll(rb_const_get(thrift_compact_protocol_class, rb_intern("TYPE_MASK")));
TYPE_BITS = rb_num2ll(rb_const_get(thrift_compact_protocol_class, rb_intern("TYPE_BITS")));
TYPE_SHIFT_AMOUNT = FIX2INT(rb_const_get(thrift_compact_protocol_class, rb_intern("TYPE_SHIFT_AMOUNT")));
PROTOCOL_ID = FIX2INT(rb_const_get(thrift_compact_protocol_class, rb_intern("PROTOCOL_ID")));
last_field_id = rb_intern("@last_field");
boolean_field_id = rb_intern("@boolean_field");
bool_value_id = rb_intern("@bool_value");
rbuf_ivar_id = rb_intern("@rbuf");
}
static void Init_rb_methods() {
rb_define_method(thrift_compact_protocol_class, "native?", rb_thrift_compact_proto_native_qmark, 0);
rb_define_method(thrift_compact_protocol_class, "write_message_begin", rb_thrift_compact_proto_write_message_begin, 3);
rb_define_method(thrift_compact_protocol_class, "write_field_begin", rb_thrift_compact_proto_write_field_begin, 3);
rb_define_method(thrift_compact_protocol_class, "write_field_stop", rb_thrift_compact_proto_write_field_stop, 0);
rb_define_method(thrift_compact_protocol_class, "write_map_begin", rb_thrift_compact_proto_write_map_begin, 3);
rb_define_method(thrift_compact_protocol_class, "write_list_begin", rb_thrift_compact_proto_write_list_begin, 2);
rb_define_method(thrift_compact_protocol_class, "write_set_begin", rb_thrift_compact_proto_write_set_begin, 2);
rb_define_method(thrift_compact_protocol_class, "write_byte", rb_thrift_compact_proto_write_byte, 1);
rb_define_method(thrift_compact_protocol_class, "write_bool", rb_thrift_compact_proto_write_bool, 1);
rb_define_method(thrift_compact_protocol_class, "write_i16", rb_thrift_compact_proto_write_i16, 1);
rb_define_method(thrift_compact_protocol_class, "write_i32", rb_thrift_compact_proto_write_i32, 1);
rb_define_method(thrift_compact_protocol_class, "write_i64", rb_thrift_compact_proto_write_i64, 1);
rb_define_method(thrift_compact_protocol_class, "write_double", rb_thrift_compact_proto_write_double, 1);
rb_define_method(thrift_compact_protocol_class, "write_string", rb_thrift_compact_proto_write_string, 1);
rb_define_method(thrift_compact_protocol_class, "write_binary", rb_thrift_compact_proto_write_binary, 1);
rb_define_method(thrift_compact_protocol_class, "write_message_end", rb_thrift_compact_proto_write_message_end, 0);
rb_define_method(thrift_compact_protocol_class, "write_struct_begin", rb_thrift_compact_proto_write_struct_begin, 1);
rb_define_method(thrift_compact_protocol_class, "write_struct_end", rb_thrift_compact_proto_write_struct_end, 0);
rb_define_method(thrift_compact_protocol_class, "write_field_end", rb_thrift_compact_proto_write_field_end, 0);
rb_define_method(thrift_compact_protocol_class, "write_map_end", rb_thrift_compact_proto_write_map_end, 0);
rb_define_method(thrift_compact_protocol_class, "write_list_end", rb_thrift_compact_proto_write_list_end, 0);
rb_define_method(thrift_compact_protocol_class, "write_set_end", rb_thrift_compact_proto_write_set_end, 0);
rb_define_method(thrift_compact_protocol_class, "read_message_begin", rb_thrift_compact_proto_read_message_begin, 0);
rb_define_method(thrift_compact_protocol_class, "read_field_begin", rb_thrift_compact_proto_read_field_begin, 0);
rb_define_method(thrift_compact_protocol_class, "read_map_begin", rb_thrift_compact_proto_read_map_begin, 0);
rb_define_method(thrift_compact_protocol_class, "read_list_begin", rb_thrift_compact_proto_read_list_begin, 0);
rb_define_method(thrift_compact_protocol_class, "read_set_begin", rb_thrift_compact_proto_read_set_begin, 0);
rb_define_method(thrift_compact_protocol_class, "read_byte", rb_thrift_compact_proto_read_byte, 0);
rb_define_method(thrift_compact_protocol_class, "read_bool", rb_thrift_compact_proto_read_bool, 0);
rb_define_method(thrift_compact_protocol_class, "read_i16", rb_thrift_compact_proto_read_i16, 0);
rb_define_method(thrift_compact_protocol_class, "read_i32", rb_thrift_compact_proto_read_i32, 0);
rb_define_method(thrift_compact_protocol_class, "read_i64", rb_thrift_compact_proto_read_i64, 0);
rb_define_method(thrift_compact_protocol_class, "read_double", rb_thrift_compact_proto_read_double, 0);
rb_define_method(thrift_compact_protocol_class, "read_string", rb_thrift_compact_proto_read_string, 0);
rb_define_method(thrift_compact_protocol_class, "read_binary", rb_thrift_compact_proto_read_binary, 0);
rb_define_method(thrift_compact_protocol_class, "read_message_end", rb_thrift_compact_proto_read_message_end, 0);
rb_define_method(thrift_compact_protocol_class, "read_struct_begin", rb_thrift_compact_proto_read_struct_begin, 0);
rb_define_method(thrift_compact_protocol_class, "read_struct_end", rb_thrift_compact_proto_read_struct_end, 0);
rb_define_method(thrift_compact_protocol_class, "read_field_end", rb_thrift_compact_proto_read_field_end, 0);
rb_define_method(thrift_compact_protocol_class, "read_map_end", rb_thrift_compact_proto_read_map_end, 0);
rb_define_method(thrift_compact_protocol_class, "read_list_end", rb_thrift_compact_proto_read_list_end, 0);
rb_define_method(thrift_compact_protocol_class, "read_set_end", rb_thrift_compact_proto_read_set_end, 0);
}
void Init_compact_protocol() {
Init_constants();
Init_rb_methods();
}

View file

@ -0,0 +1,20 @@
/*
* 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.
*/
void Init_compact_protocol();

View file

@ -0,0 +1,99 @@
/*
* 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.
*/
extern int TTYPE_STOP;
extern int TTYPE_BOOL;
extern int TTYPE_BYTE;
extern int TTYPE_I16;
extern int TTYPE_I32;
extern int TTYPE_I64;
extern int TTYPE_DOUBLE;
extern int TTYPE_STRING;
extern int TTYPE_MAP;
extern int TTYPE_SET;
extern int TTYPE_LIST;
extern int TTYPE_STRUCT;
extern ID validate_method_id;
extern ID write_struct_begin_method_id;
extern ID write_struct_end_method_id;
extern ID write_field_begin_method_id;
extern ID write_field_end_method_id;
extern ID write_boolean_method_id;
extern ID write_byte_method_id;
extern ID write_i16_method_id;
extern ID write_i32_method_id;
extern ID write_i64_method_id;
extern ID write_double_method_id;
extern ID write_string_method_id;
extern ID write_binary_method_id;
extern ID write_map_begin_method_id;
extern ID write_map_end_method_id;
extern ID write_list_begin_method_id;
extern ID write_list_end_method_id;
extern ID write_set_begin_method_id;
extern ID write_set_end_method_id;
extern ID read_bool_method_id;
extern ID read_byte_method_id;
extern ID read_i16_method_id;
extern ID read_i32_method_id;
extern ID read_i64_method_id;
extern ID read_string_method_id;
extern ID read_binary_method_id;
extern ID read_double_method_id;
extern ID read_map_begin_method_id;
extern ID read_map_end_method_id;
extern ID read_list_begin_method_id;
extern ID read_list_end_method_id;
extern ID read_set_begin_method_id;
extern ID read_set_end_method_id;
extern ID read_struct_begin_method_id;
extern ID read_struct_end_method_id;
extern ID read_field_begin_method_id;
extern ID read_field_end_method_id;
extern ID keys_method_id;
extern ID entries_method_id;
extern ID write_field_stop_method_id;
extern ID skip_method_id;
extern ID write_method_id;
extern ID read_all_method_id;
extern ID read_into_buffer_method_id;
extern ID force_binary_encoding_id;
extern ID convert_to_utf8_byte_buffer_id;
extern ID convert_to_string_id;
extern ID fields_const_id;
extern ID transport_ivar_id;
extern ID strict_read_ivar_id;
extern ID strict_write_ivar_id;
extern VALUE type_sym;
extern VALUE name_sym;
extern VALUE key_sym;
extern VALUE value_sym;
extern VALUE element_sym;
extern VALUE class_sym;
extern VALUE binary_sym;
extern VALUE rb_cSet;
extern VALUE thrift_module;
extern VALUE thrift_types_module;
extern VALUE thrift_bytes_module;
extern VALUE class_thrift_protocol;
extern VALUE protocol_exception_class;

View file

@ -0,0 +1,34 @@
#
# 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.
#
if defined?(RUBY_ENGINE) && RUBY_ENGINE =~ /jruby/
File.open('Makefile', 'w'){|f| f.puts "all:\n\ninstall:\n" }
else
require 'mkmf'
require 'rbconfig'
$ARCH_FLAGS = RbConfig::CONFIG['CFLAGS'].scan( /(-arch )(\S+)/ ).map{|x,y| x + y + ' ' }.join('')
$CFLAGS = "-fsigned-char -g -O2 -Wall -Werror " + $ARCH_FLAGS
have_func("strlcpy", "string.h")
create_makefile 'thrift_native'
end

View file

@ -0,0 +1,41 @@
/*
* 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.
*/
#define GET_TRANSPORT(obj) rb_ivar_get(obj, transport_ivar_id)
#define GET_STRICT_READ(obj) rb_ivar_get(obj, strict_read_ivar_id)
#define GET_STRICT_WRITE(obj) rb_ivar_get(obj, strict_write_ivar_id)
#define WRITE(obj, data, length) rb_funcall(obj, write_method_id, 1, rb_str_new(data, length))
#define CHECK_NIL(obj) if (NIL_P(obj)) { rb_raise(rb_eStandardError, "nil argument not allowed!");}
#define READ(obj, length) rb_funcall(GET_TRANSPORT(obj), read_all_method_id, 1, INT2FIX(length))
#ifndef RFLOAT_VALUE
# define RFLOAT_VALUE(v) RFLOAT(rb_Float(v))->value
#endif
#ifndef RSTRING_LEN
# define RSTRING_LEN(v) RSTRING(rb_String(v))->len
#endif
#ifndef RSTRING_PTR
# define RSTRING_PTR(v) RSTRING(rb_String(v))->ptr
#endif
#ifndef RARRAY_LEN
# define RARRAY_LEN(v) RARRAY(rb_Array(v))->len
#endif

View file

@ -0,0 +1,134 @@
/**
* 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.
*/
#include <ruby.h>
#include <constants.h>
#include <bytes.h>
#include <macros.h>
ID buf_ivar_id;
ID index_ivar_id;
ID slice_method_id;
int GARBAGE_BUFFER_SIZE;
#define GET_BUF(self) rb_ivar_get(self, buf_ivar_id)
VALUE rb_thrift_memory_buffer_write(VALUE self, VALUE str);
VALUE rb_thrift_memory_buffer_read(VALUE self, VALUE length_value);
VALUE rb_thrift_memory_buffer_read_byte(VALUE self);
VALUE rb_thrift_memory_buffer_read_into_buffer(VALUE self, VALUE buffer_value, VALUE size_value);
VALUE rb_thrift_memory_buffer_write(VALUE self, VALUE str) {
VALUE buf = GET_BUF(self);
str = force_binary_encoding(str);
rb_str_buf_cat(buf, StringValuePtr(str), RSTRING_LEN(str));
return Qnil;
}
VALUE rb_thrift_memory_buffer_read(VALUE self, VALUE length_value) {
int length = FIX2INT(length_value);
VALUE index_value = rb_ivar_get(self, index_ivar_id);
int index = FIX2INT(index_value);
VALUE buf = GET_BUF(self);
VALUE data = rb_funcall(buf, slice_method_id, 2, index_value, length_value);
index += length;
if (index > RSTRING_LEN(buf)) {
index = RSTRING_LEN(buf);
}
if (index >= GARBAGE_BUFFER_SIZE) {
rb_ivar_set(self, buf_ivar_id, rb_funcall(buf, slice_method_id, 2, INT2FIX(index), INT2FIX(RSTRING_LEN(buf) - 1)));
index = 0;
}
rb_ivar_set(self, index_ivar_id, INT2FIX(index));
if (RSTRING_LEN(data) < length) {
rb_raise(rb_eEOFError, "Not enough bytes remain in memory buffer");
}
return data;
}
VALUE rb_thrift_memory_buffer_read_byte(VALUE self) {
VALUE index_value = rb_ivar_get(self, index_ivar_id);
int index = FIX2INT(index_value);
VALUE buf = GET_BUF(self);
if (index >= RSTRING_LEN(buf)) {
rb_raise(rb_eEOFError, "Not enough bytes remain in memory buffer");
}
char byte = RSTRING_PTR(buf)[index++];
if (index >= GARBAGE_BUFFER_SIZE) {
rb_ivar_set(self, buf_ivar_id, rb_funcall(buf, slice_method_id, 2, INT2FIX(index), INT2FIX(RSTRING_LEN(buf) - 1)));
index = 0;
}
rb_ivar_set(self, index_ivar_id, INT2FIX(index));
int result = (int) byte;
return INT2FIX(result);
}
VALUE rb_thrift_memory_buffer_read_into_buffer(VALUE self, VALUE buffer_value, VALUE size_value) {
int i = 0;
int size = FIX2INT(size_value);
int index;
VALUE buf = GET_BUF(self);
index = FIX2INT(rb_ivar_get(self, index_ivar_id));
while (i < size) {
if (index >= RSTRING_LEN(buf)) {
rb_raise(rb_eEOFError, "Not enough bytes remain in memory buffer");
}
char byte = RSTRING_PTR(buf)[index++];
if (i >= RSTRING_LEN(buffer_value)) {
rb_raise(rb_eIndexError, "index %d out of string", i);
}
((char*)RSTRING_PTR(buffer_value))[i] = byte;
i++;
}
if (index >= GARBAGE_BUFFER_SIZE) {
rb_ivar_set(self, buf_ivar_id, rb_funcall(buf, slice_method_id, 2, INT2FIX(index), INT2FIX(RSTRING_LEN(buf) - 1)));
index = 0;
}
rb_ivar_set(self, index_ivar_id, INT2FIX(index));
return INT2FIX(i);
}
void Init_memory_buffer() {
VALUE thrift_memory_buffer_class = rb_const_get(thrift_module, rb_intern("MemoryBufferTransport"));
rb_define_method(thrift_memory_buffer_class, "write", rb_thrift_memory_buffer_write, 1);
rb_define_method(thrift_memory_buffer_class, "read", rb_thrift_memory_buffer_read, 1);
rb_define_method(thrift_memory_buffer_class, "read_byte", rb_thrift_memory_buffer_read_byte, 0);
rb_define_method(thrift_memory_buffer_class, "read_into_buffer", rb_thrift_memory_buffer_read_into_buffer, 2);
buf_ivar_id = rb_intern("@buf");
index_ivar_id = rb_intern("@index");
slice_method_id = rb_intern("slice");
GARBAGE_BUFFER_SIZE = FIX2INT(rb_const_get(thrift_memory_buffer_class, rb_intern("GARBAGE_BUFFER_SIZE")));
}

View file

@ -0,0 +1,20 @@
/*
* 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.
*/
void Init_memory_buffer();

View file

View file

View file

@ -0,0 +1,41 @@
/**
* 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.
*/
#include "strlcpy.h"
#ifndef HAVE_STRLCPY
#define HAVE_STRLCPY
size_t
strlcpy (char *dst, const char *src, size_t dst_sz)
{
size_t n;
for (n = 0; n < dst_sz; n++) {
if ((*dst++ = *src++) == '\0')
break;
}
if (n < dst_sz)
return n;
if (n > 0)
*(dst - 1) = '\0';
return n + strlen (src);
}
#endif

View file

@ -0,0 +1,34 @@
/*
* 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.
*/
#include <sys/types.h>
#include <string.h>
#ifndef __has_builtin
#define __has_builtin(x) 0
#endif
#ifndef HAVE_STRLCPY
size_t strlcpy (char *dst, const char *src, size_t dst_sz);
#else
#if !__has_builtin(strlcpy)
extern size_t strlcpy(char *, const char *, size_t);
#endif
#endif

View file

@ -0,0 +1,711 @@
/**
* 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.
*/
#include "struct.h"
#include "constants.h"
#include "macros.h"
#include "strlcpy.h"
VALUE thrift_union_class;
ID setfield_id;
ID setvalue_id;
ID to_s_method_id;
ID name_to_id_method_id;
static ID sorted_field_ids_method_id;
#define IS_CONTAINER(ttype) ((ttype) == TTYPE_MAP || (ttype) == TTYPE_LIST || (ttype) == TTYPE_SET)
#define STRUCT_FIELDS(obj) rb_const_get(CLASS_OF(obj), fields_const_id)
//-------------------------------------------
// Writing section
//-------------------------------------------
// default fn pointers for protocol stuff here
VALUE default_write_bool(VALUE protocol, VALUE value) {
rb_funcall(protocol, write_boolean_method_id, 1, value);
return Qnil;
}
VALUE default_write_byte(VALUE protocol, VALUE value) {
rb_funcall(protocol, write_byte_method_id, 1, value);
return Qnil;
}
VALUE default_write_i16(VALUE protocol, VALUE value) {
rb_funcall(protocol, write_i16_method_id, 1, value);
return Qnil;
}
VALUE default_write_i32(VALUE protocol, VALUE value) {
rb_funcall(protocol, write_i32_method_id, 1, value);
return Qnil;
}
VALUE default_write_i64(VALUE protocol, VALUE value) {
rb_funcall(protocol, write_i64_method_id, 1, value);
return Qnil;
}
VALUE default_write_double(VALUE protocol, VALUE value) {
rb_funcall(protocol, write_double_method_id, 1, value);
return Qnil;
}
VALUE default_write_string(VALUE protocol, VALUE value) {
rb_funcall(protocol, write_string_method_id, 1, value);
return Qnil;
}
VALUE default_write_binary(VALUE protocol, VALUE value) {
rb_funcall(protocol, write_binary_method_id, 1, value);
return Qnil;
}
VALUE default_write_list_begin(VALUE protocol, VALUE etype, VALUE length) {
rb_funcall(protocol, write_list_begin_method_id, 2, etype, length);
return Qnil;
}
VALUE default_write_list_end(VALUE protocol) {
rb_funcall(protocol, write_list_end_method_id, 0);
return Qnil;
}
VALUE default_write_set_begin(VALUE protocol, VALUE etype, VALUE length) {
rb_funcall(protocol, write_set_begin_method_id, 2, etype, length);
return Qnil;
}
VALUE default_write_set_end(VALUE protocol) {
rb_funcall(protocol, write_set_end_method_id, 0);
return Qnil;
}
VALUE default_write_map_begin(VALUE protocol, VALUE ktype, VALUE vtype, VALUE length) {
rb_funcall(protocol, write_map_begin_method_id, 3, ktype, vtype, length);
return Qnil;
}
VALUE default_write_map_end(VALUE protocol) {
rb_funcall(protocol, write_map_end_method_id, 0);
return Qnil;
}
VALUE default_write_struct_begin(VALUE protocol, VALUE struct_name) {
rb_funcall(protocol, write_struct_begin_method_id, 1, struct_name);
return Qnil;
}
VALUE default_write_struct_end(VALUE protocol) {
rb_funcall(protocol, write_struct_end_method_id, 0);
return Qnil;
}
VALUE default_write_field_begin(VALUE protocol, VALUE name, VALUE type, VALUE id) {
rb_funcall(protocol, write_field_begin_method_id, 3, name, type, id);
return Qnil;
}
VALUE default_write_field_end(VALUE protocol) {
rb_funcall(protocol, write_field_end_method_id, 0);
return Qnil;
}
VALUE default_write_field_stop(VALUE protocol) {
rb_funcall(protocol, write_field_stop_method_id, 0);
return Qnil;
}
VALUE default_read_field_begin(VALUE protocol) {
return rb_funcall(protocol, read_field_begin_method_id, 0);
}
VALUE default_read_field_end(VALUE protocol) {
return rb_funcall(protocol, read_field_end_method_id, 0);
}
VALUE default_read_map_begin(VALUE protocol) {
return rb_funcall(protocol, read_map_begin_method_id, 0);
}
VALUE default_read_map_end(VALUE protocol) {
return rb_funcall(protocol, read_map_end_method_id, 0);
}
VALUE default_read_list_begin(VALUE protocol) {
return rb_funcall(protocol, read_list_begin_method_id, 0);
}
VALUE default_read_list_end(VALUE protocol) {
return rb_funcall(protocol, read_list_end_method_id, 0);
}
VALUE default_read_set_begin(VALUE protocol) {
return rb_funcall(protocol, read_set_begin_method_id, 0);
}
VALUE default_read_set_end(VALUE protocol) {
return rb_funcall(protocol, read_set_end_method_id, 0);
}
VALUE default_read_byte(VALUE protocol) {
return rb_funcall(protocol, read_byte_method_id, 0);
}
VALUE default_read_bool(VALUE protocol) {
return rb_funcall(protocol, read_bool_method_id, 0);
}
VALUE default_read_i16(VALUE protocol) {
return rb_funcall(protocol, read_i16_method_id, 0);
}
VALUE default_read_i32(VALUE protocol) {
return rb_funcall(protocol, read_i32_method_id, 0);
}
VALUE default_read_i64(VALUE protocol) {
return rb_funcall(protocol, read_i64_method_id, 0);
}
VALUE default_read_double(VALUE protocol) {
return rb_funcall(protocol, read_double_method_id, 0);
}
VALUE default_read_string(VALUE protocol) {
return rb_funcall(protocol, read_string_method_id, 0);
}
VALUE default_read_binary(VALUE protocol) {
return rb_funcall(protocol, read_binary_method_id, 0);
}
VALUE default_read_struct_begin(VALUE protocol) {
return rb_funcall(protocol, read_struct_begin_method_id, 0);
}
VALUE default_read_struct_end(VALUE protocol) {
return rb_funcall(protocol, read_struct_end_method_id, 0);
}
// end default protocol methods
static VALUE rb_thrift_union_write (VALUE self, VALUE protocol);
static VALUE rb_thrift_struct_write(VALUE self, VALUE protocol);
static void write_anything(int ttype, VALUE value, VALUE protocol, VALUE field_info);
VALUE get_field_value(VALUE obj, VALUE field_name) {
char name_buf[RSTRING_LEN(field_name) + 2];
name_buf[0] = '@';
strlcpy(&name_buf[1], RSTRING_PTR(field_name), RSTRING_LEN(field_name) + 1);
VALUE value = rb_ivar_get(obj, rb_intern(name_buf));
return value;
}
static void write_container(int ttype, VALUE field_info, VALUE value, VALUE protocol) {
int sz, i;
if (ttype == TTYPE_MAP) {
VALUE keys;
VALUE key;
VALUE val;
Check_Type(value, T_HASH);
VALUE key_info = rb_hash_aref(field_info, key_sym);
VALUE keytype_value = rb_hash_aref(key_info, type_sym);
int keytype = FIX2INT(keytype_value);
VALUE value_info = rb_hash_aref(field_info, value_sym);
VALUE valuetype_value = rb_hash_aref(value_info, type_sym);
int valuetype = FIX2INT(valuetype_value);
keys = rb_funcall(value, keys_method_id, 0);
sz = RARRAY_LEN(keys);
default_write_map_begin(protocol, keytype_value, valuetype_value, INT2FIX(sz));
for (i = 0; i < sz; i++) {
key = rb_ary_entry(keys, i);
val = rb_hash_aref(value, key);
if (IS_CONTAINER(keytype)) {
write_container(keytype, key_info, key, protocol);
} else {
write_anything(keytype, key, protocol, key_info);
}
if (IS_CONTAINER(valuetype)) {
write_container(valuetype, value_info, val, protocol);
} else {
write_anything(valuetype, val, protocol, value_info);
}
}
default_write_map_end(protocol);
} else if (ttype == TTYPE_LIST) {
Check_Type(value, T_ARRAY);
sz = RARRAY_LEN(value);
VALUE element_type_info = rb_hash_aref(field_info, element_sym);
VALUE element_type_value = rb_hash_aref(element_type_info, type_sym);
int element_type = FIX2INT(element_type_value);
default_write_list_begin(protocol, element_type_value, INT2FIX(sz));
for (i = 0; i < sz; ++i) {
VALUE val = rb_ary_entry(value, i);
if (IS_CONTAINER(element_type)) {
write_container(element_type, element_type_info, val, protocol);
} else {
write_anything(element_type, val, protocol, element_type_info);
}
}
default_write_list_end(protocol);
} else if (ttype == TTYPE_SET) {
VALUE items;
if (TYPE(value) == T_ARRAY) {
items = value;
} else {
if (rb_cSet == CLASS_OF(value)) {
items = rb_funcall(value, entries_method_id, 0);
} else {
Check_Type(value, T_HASH);
items = rb_funcall(value, keys_method_id, 0);
}
}
sz = RARRAY_LEN(items);
VALUE element_type_info = rb_hash_aref(field_info, element_sym);
VALUE element_type_value = rb_hash_aref(element_type_info, type_sym);
int element_type = FIX2INT(element_type_value);
default_write_set_begin(protocol, element_type_value, INT2FIX(sz));
for (i = 0; i < sz; i++) {
VALUE val = rb_ary_entry(items, i);
if (IS_CONTAINER(element_type)) {
write_container(element_type, element_type_info, val, protocol);
} else {
write_anything(element_type, val, protocol, element_type_info);
}
}
default_write_set_end(protocol);
} else {
rb_raise(rb_eNotImpError, "can't write container of type: %d", ttype);
}
}
static void write_anything(int ttype, VALUE value, VALUE protocol, VALUE field_info) {
if (ttype == TTYPE_BOOL) {
default_write_bool(protocol, value);
} else if (ttype == TTYPE_BYTE) {
default_write_byte(protocol, value);
} else if (ttype == TTYPE_I16) {
default_write_i16(protocol, value);
} else if (ttype == TTYPE_I32) {
default_write_i32(protocol, value);
} else if (ttype == TTYPE_I64) {
default_write_i64(protocol, value);
} else if (ttype == TTYPE_DOUBLE) {
default_write_double(protocol, value);
} else if (ttype == TTYPE_STRING) {
VALUE is_binary = rb_hash_aref(field_info, binary_sym);
if (is_binary != Qtrue) {
default_write_string(protocol, value);
} else {
default_write_binary(protocol, value);
}
} else if (IS_CONTAINER(ttype)) {
write_container(ttype, field_info, value, protocol);
} else if (ttype == TTYPE_STRUCT) {
if (rb_obj_is_kind_of(value, thrift_union_class)) {
rb_thrift_union_write(value, protocol);
} else {
rb_thrift_struct_write(value, protocol);
}
} else {
rb_raise(rb_eNotImpError, "Unknown type for binary_encoding: %d", ttype);
}
}
static VALUE rb_thrift_struct_write(VALUE self, VALUE protocol) {
// call validate
rb_funcall(self, validate_method_id, 0);
// write struct begin
default_write_struct_begin(protocol, rb_class_name(CLASS_OF(self)));
// iterate through all the fields here
VALUE struct_fields = STRUCT_FIELDS(self);
VALUE sorted_field_ids = rb_funcall(self, sorted_field_ids_method_id, 0);
int i = 0;
for (i=0; i < RARRAY_LEN(sorted_field_ids); i++) {
VALUE field_id = rb_ary_entry(sorted_field_ids, i);
VALUE field_info = rb_hash_aref(struct_fields, field_id);
VALUE ttype_value = rb_hash_aref(field_info, type_sym);
int ttype = FIX2INT(ttype_value);
VALUE field_name = rb_hash_aref(field_info, name_sym);
VALUE field_value = get_field_value(self, field_name);
if (!NIL_P(field_value)) {
default_write_field_begin(protocol, field_name, ttype_value, field_id);
write_anything(ttype, field_value, protocol, field_info);
default_write_field_end(protocol);
}
}
default_write_field_stop(protocol);
// write struct end
default_write_struct_end(protocol);
return Qnil;
}
//-------------------------------------------
// Reading section
//-------------------------------------------
static VALUE rb_thrift_union_read(VALUE self, VALUE protocol);
static VALUE rb_thrift_struct_read(VALUE self, VALUE protocol);
static void skip_map_contents(VALUE protocol, VALUE key_type_value, VALUE value_type_value, int size);
static void skip_list_or_set_contents(VALUE protocol, VALUE element_type_value, int size);
static void set_field_value(VALUE obj, VALUE field_name, VALUE value) {
char name_buf[RSTRING_LEN(field_name) + 2];
name_buf[0] = '@';
strlcpy(&name_buf[1], RSTRING_PTR(field_name), RSTRING_LEN(field_name)+1);
rb_ivar_set(obj, rb_intern(name_buf), value);
}
// Helper method to skip the contents of a map (assumes the map header has been read).
static void skip_map_contents(VALUE protocol, VALUE key_type_value, VALUE value_type_value, int size) {
int i;
for (i = 0; i < size; i++) {
rb_funcall(protocol, skip_method_id, 1, key_type_value);
rb_funcall(protocol, skip_method_id, 1, value_type_value);
}
}
// Helper method to skip the contents of a list or set (assumes the list/set header has been read).
static void skip_list_or_set_contents(VALUE protocol, VALUE element_type_value, int size) {
int i;
for (i = 0; i < size; i++) {
rb_funcall(protocol, skip_method_id, 1, element_type_value);
}
}
static VALUE read_anything(VALUE protocol, int ttype, VALUE field_info) {
VALUE result = Qnil;
if (ttype == TTYPE_BOOL) {
result = default_read_bool(protocol);
} else if (ttype == TTYPE_BYTE) {
result = default_read_byte(protocol);
} else if (ttype == TTYPE_I16) {
result = default_read_i16(protocol);
} else if (ttype == TTYPE_I32) {
result = default_read_i32(protocol);
} else if (ttype == TTYPE_I64) {
result = default_read_i64(protocol);
} else if (ttype == TTYPE_STRING) {
VALUE is_binary = rb_hash_aref(field_info, binary_sym);
if (is_binary != Qtrue) {
result = default_read_string(protocol);
} else {
result = default_read_binary(protocol);
}
} else if (ttype == TTYPE_DOUBLE) {
result = default_read_double(protocol);
} else if (ttype == TTYPE_STRUCT) {
VALUE klass = rb_hash_aref(field_info, class_sym);
result = rb_class_new_instance(0, NULL, klass);
if (rb_obj_is_kind_of(result, thrift_union_class)) {
rb_thrift_union_read(result, protocol);
} else {
rb_thrift_struct_read(result, protocol);
}
} else if (ttype == TTYPE_MAP) {
int i;
VALUE map_header = default_read_map_begin(protocol);
int key_ttype = FIX2INT(rb_ary_entry(map_header, 0));
int value_ttype = FIX2INT(rb_ary_entry(map_header, 1));
int num_entries = FIX2INT(rb_ary_entry(map_header, 2));
// Check the declared key and value types against the expected ones and skip the map contents
// if the types don't match.
VALUE key_info = rb_hash_aref(field_info, key_sym);
VALUE value_info = rb_hash_aref(field_info, value_sym);
if (!NIL_P(key_info) && !NIL_P(value_info)) {
int specified_key_type = FIX2INT(rb_hash_aref(key_info, type_sym));
int specified_value_type = FIX2INT(rb_hash_aref(value_info, type_sym));
if (num_entries == 0 || (specified_key_type == key_ttype && specified_value_type == value_ttype)) {
result = rb_hash_new();
for (i = 0; i < num_entries; ++i) {
VALUE key, val;
key = read_anything(protocol, key_ttype, key_info);
val = read_anything(protocol, value_ttype, value_info);
rb_hash_aset(result, key, val);
}
} else {
skip_map_contents(protocol, INT2FIX(key_ttype), INT2FIX(value_ttype), num_entries);
}
} else {
skip_map_contents(protocol, INT2FIX(key_ttype), INT2FIX(value_ttype), num_entries);
}
default_read_map_end(protocol);
} else if (ttype == TTYPE_LIST) {
int i;
VALUE list_header = default_read_list_begin(protocol);
int element_ttype = FIX2INT(rb_ary_entry(list_header, 0));
int num_elements = FIX2INT(rb_ary_entry(list_header, 1));
// Check the declared element type against the expected one and skip the list contents
// if the types don't match.
VALUE element_info = rb_hash_aref(field_info, element_sym);
if (!NIL_P(element_info)) {
int specified_element_type = FIX2INT(rb_hash_aref(element_info, type_sym));
if (specified_element_type == element_ttype) {
result = rb_ary_new2(num_elements);
for (i = 0; i < num_elements; ++i) {
rb_ary_push(result, read_anything(protocol, element_ttype, rb_hash_aref(field_info, element_sym)));
}
} else {
skip_list_or_set_contents(protocol, INT2FIX(element_ttype), num_elements);
}
} else {
skip_list_or_set_contents(protocol, INT2FIX(element_ttype), num_elements);
}
default_read_list_end(protocol);
} else if (ttype == TTYPE_SET) {
VALUE items;
int i;
VALUE set_header = default_read_set_begin(protocol);
int element_ttype = FIX2INT(rb_ary_entry(set_header, 0));
int num_elements = FIX2INT(rb_ary_entry(set_header, 1));
// Check the declared element type against the expected one and skip the set contents
// if the types don't match.
VALUE element_info = rb_hash_aref(field_info, element_sym);
if (!NIL_P(element_info)) {
int specified_element_type = FIX2INT(rb_hash_aref(element_info, type_sym));
if (specified_element_type == element_ttype) {
items = rb_ary_new2(num_elements);
for (i = 0; i < num_elements; ++i) {
rb_ary_push(items, read_anything(protocol, element_ttype, rb_hash_aref(field_info, element_sym)));
}
result = rb_class_new_instance(1, &items, rb_cSet);
} else {
skip_list_or_set_contents(protocol, INT2FIX(element_ttype), num_elements);
}
} else {
skip_list_or_set_contents(protocol, INT2FIX(element_ttype), num_elements);
}
default_read_set_end(protocol);
} else {
rb_raise(rb_eNotImpError, "read_anything not implemented for type %d!", ttype);
}
return result;
}
static VALUE rb_thrift_struct_read(VALUE self, VALUE protocol) {
// read struct begin
default_read_struct_begin(protocol);
VALUE struct_fields = STRUCT_FIELDS(self);
// read each field
while (true) {
VALUE field_header = default_read_field_begin(protocol);
VALUE field_type_value = rb_ary_entry(field_header, 1);
int field_type = FIX2INT(field_type_value);
if (field_type == TTYPE_STOP) {
break;
}
// make sure we got a type we expected
VALUE field_info = rb_hash_aref(struct_fields, rb_ary_entry(field_header, 2));
if (!NIL_P(field_info)) {
int specified_type = FIX2INT(rb_hash_aref(field_info, type_sym));
if (field_type == specified_type) {
// read the value
VALUE name = rb_hash_aref(field_info, name_sym);
set_field_value(self, name, read_anything(protocol, field_type, field_info));
} else {
rb_funcall(protocol, skip_method_id, 1, field_type_value);
}
} else {
rb_funcall(protocol, skip_method_id, 1, field_type_value);
}
// read field end
default_read_field_end(protocol);
}
// read struct end
default_read_struct_end(protocol);
// call validate
rb_funcall(self, validate_method_id, 0);
return Qnil;
}
// --------------------------------
// Union section
// --------------------------------
static VALUE rb_thrift_union_read(VALUE self, VALUE protocol) {
// read struct begin
default_read_struct_begin(protocol);
VALUE struct_fields = STRUCT_FIELDS(self);
VALUE field_header = default_read_field_begin(protocol);
VALUE field_type_value = rb_ary_entry(field_header, 1);
int field_type = FIX2INT(field_type_value);
// make sure we got a type we expected
VALUE field_info = rb_hash_aref(struct_fields, rb_ary_entry(field_header, 2));
if (!NIL_P(field_info)) {
int specified_type = FIX2INT(rb_hash_aref(field_info, type_sym));
if (field_type == specified_type) {
// read the value
VALUE name = rb_hash_aref(field_info, name_sym);
rb_iv_set(self, "@setfield", rb_str_intern(name));
rb_iv_set(self, "@value", read_anything(protocol, field_type, field_info));
} else {
rb_funcall(protocol, skip_method_id, 1, field_type_value);
}
} else {
rb_funcall(protocol, skip_method_id, 1, field_type_value);
}
// read field end
default_read_field_end(protocol);
field_header = default_read_field_begin(protocol);
field_type_value = rb_ary_entry(field_header, 1);
field_type = FIX2INT(field_type_value);
if (field_type != TTYPE_STOP) {
rb_raise(rb_eRuntimeError, "too many fields in union!");
}
// read struct end
default_read_struct_end(protocol);
// call validate
rb_funcall(self, validate_method_id, 0);
return Qnil;
}
static VALUE rb_thrift_union_write(VALUE self, VALUE protocol) {
// call validate
rb_funcall(self, validate_method_id, 0);
// write struct begin
default_write_struct_begin(protocol, rb_class_name(CLASS_OF(self)));
VALUE struct_fields = STRUCT_FIELDS(self);
VALUE setfield = rb_ivar_get(self, setfield_id);
VALUE setvalue = rb_ivar_get(self, setvalue_id);
VALUE field_id = rb_funcall(self, name_to_id_method_id, 1, rb_funcall(setfield, to_s_method_id, 0));
VALUE field_info = rb_hash_aref(struct_fields, field_id);
if(NIL_P(field_info)) {
rb_raise(rb_eRuntimeError, "set_field is not valid for this union!");
}
VALUE ttype_value = rb_hash_aref(field_info, type_sym);
int ttype = FIX2INT(ttype_value);
default_write_field_begin(protocol, setfield, ttype_value, field_id);
write_anything(ttype, setvalue, protocol, field_info);
default_write_field_end(protocol);
default_write_field_stop(protocol);
// write struct end
default_write_struct_end(protocol);
return Qnil;
}
void Init_struct() {
VALUE struct_module = rb_const_get(thrift_module, rb_intern("Struct"));
rb_define_method(struct_module, "write", rb_thrift_struct_write, 1);
rb_define_method(struct_module, "read", rb_thrift_struct_read, 1);
thrift_union_class = rb_const_get(thrift_module, rb_intern("Union"));
rb_define_method(thrift_union_class, "write", rb_thrift_union_write, 1);
rb_define_method(thrift_union_class, "read", rb_thrift_union_read, 1);
setfield_id = rb_intern("@setfield");
setvalue_id = rb_intern("@value");
to_s_method_id = rb_intern("to_s");
name_to_id_method_id = rb_intern("name_to_id");
sorted_field_ids_method_id = rb_intern("sorted_field_ids");
}

View file

@ -0,0 +1,25 @@
/*
* 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.
*/
#include <stdbool.h>
#include <ruby.h>
void Init_struct();
void Init_union();

View file

@ -0,0 +1,201 @@
/**
* 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.
*/
#include <ruby.h>
#include <bytes.h>
#include <struct.h>
#include <binary_protocol_accelerated.h>
#include <compact_protocol.h>
#include <memory_buffer.h>
// cached classes/modules
VALUE rb_cSet;
VALUE thrift_module;
VALUE thrift_bytes_module;
VALUE thrift_types_module;
// TType constants
int TTYPE_STOP;
int TTYPE_BOOL;
int TTYPE_BYTE;
int TTYPE_I16;
int TTYPE_I32;
int TTYPE_I64;
int TTYPE_DOUBLE;
int TTYPE_STRING;
int TTYPE_MAP;
int TTYPE_SET;
int TTYPE_LIST;
int TTYPE_STRUCT;
// method ids
ID validate_method_id;
ID write_struct_begin_method_id;
ID write_struct_end_method_id;
ID write_field_begin_method_id;
ID write_field_end_method_id;
ID write_boolean_method_id;
ID write_byte_method_id;
ID write_i16_method_id;
ID write_i32_method_id;
ID write_i64_method_id;
ID write_double_method_id;
ID write_string_method_id;
ID write_binary_method_id;
ID write_map_begin_method_id;
ID write_map_end_method_id;
ID write_list_begin_method_id;
ID write_list_end_method_id;
ID write_set_begin_method_id;
ID write_set_end_method_id;
ID read_bool_method_id;
ID read_byte_method_id;
ID read_i16_method_id;
ID read_i32_method_id;
ID read_i64_method_id;
ID read_string_method_id;
ID read_binary_method_id;
ID read_double_method_id;
ID read_map_begin_method_id;
ID read_map_end_method_id;
ID read_list_begin_method_id;
ID read_list_end_method_id;
ID read_set_begin_method_id;
ID read_set_end_method_id;
ID read_struct_begin_method_id;
ID read_struct_end_method_id;
ID read_field_begin_method_id;
ID read_field_end_method_id;
ID keys_method_id;
ID entries_method_id;
ID write_field_stop_method_id;
ID skip_method_id;
ID write_method_id;
ID read_all_method_id;
ID read_into_buffer_method_id;
ID force_binary_encoding_id;
ID convert_to_utf8_byte_buffer_id;
ID convert_to_string_id;
// constant ids
ID fields_const_id;
ID transport_ivar_id;
ID strict_read_ivar_id;
ID strict_write_ivar_id;
// cached symbols
VALUE type_sym;
VALUE name_sym;
VALUE key_sym;
VALUE value_sym;
VALUE element_sym;
VALUE class_sym;
VALUE binary_sym;
VALUE protocol_exception_class;
void Init_thrift_native() {
// cached classes
thrift_module = rb_const_get(rb_cObject, rb_intern("Thrift"));
thrift_bytes_module = rb_const_get(thrift_module, rb_intern("Bytes"));
thrift_types_module = rb_const_get(thrift_module, rb_intern("Types"));
rb_cSet = rb_const_get(rb_cObject, rb_intern("Set"));
protocol_exception_class = rb_const_get(thrift_module, rb_intern("ProtocolException"));
// Init ttype constants
TTYPE_BOOL = FIX2INT(rb_const_get(thrift_types_module, rb_intern("BOOL")));
TTYPE_BYTE = FIX2INT(rb_const_get(thrift_types_module, rb_intern("BYTE")));
TTYPE_I16 = FIX2INT(rb_const_get(thrift_types_module, rb_intern("I16")));
TTYPE_I32 = FIX2INT(rb_const_get(thrift_types_module, rb_intern("I32")));
TTYPE_I64 = FIX2INT(rb_const_get(thrift_types_module, rb_intern("I64")));
TTYPE_DOUBLE = FIX2INT(rb_const_get(thrift_types_module, rb_intern("DOUBLE")));
TTYPE_STRING = FIX2INT(rb_const_get(thrift_types_module, rb_intern("STRING")));
TTYPE_MAP = FIX2INT(rb_const_get(thrift_types_module, rb_intern("MAP")));
TTYPE_SET = FIX2INT(rb_const_get(thrift_types_module, rb_intern("SET")));
TTYPE_LIST = FIX2INT(rb_const_get(thrift_types_module, rb_intern("LIST")));
TTYPE_STRUCT = FIX2INT(rb_const_get(thrift_types_module, rb_intern("STRUCT")));
// method ids
validate_method_id = rb_intern("validate");
write_struct_begin_method_id = rb_intern("write_struct_begin");
write_struct_end_method_id = rb_intern("write_struct_end");
write_field_begin_method_id = rb_intern("write_field_begin");
write_field_end_method_id = rb_intern("write_field_end");
write_boolean_method_id = rb_intern("write_bool");
write_byte_method_id = rb_intern("write_byte");
write_i16_method_id = rb_intern("write_i16");
write_i32_method_id = rb_intern("write_i32");
write_i64_method_id = rb_intern("write_i64");
write_double_method_id = rb_intern("write_double");
write_string_method_id = rb_intern("write_string");
write_binary_method_id = rb_intern("write_binary");
write_map_begin_method_id = rb_intern("write_map_begin");
write_map_end_method_id = rb_intern("write_map_end");
write_list_begin_method_id = rb_intern("write_list_begin");
write_list_end_method_id = rb_intern("write_list_end");
write_set_begin_method_id = rb_intern("write_set_begin");
write_set_end_method_id = rb_intern("write_set_end");
read_bool_method_id = rb_intern("read_bool");
read_byte_method_id = rb_intern("read_byte");
read_i16_method_id = rb_intern("read_i16");
read_i32_method_id = rb_intern("read_i32");
read_i64_method_id = rb_intern("read_i64");
read_string_method_id = rb_intern("read_string");
read_binary_method_id = rb_intern("read_binary");
read_double_method_id = rb_intern("read_double");
read_map_begin_method_id = rb_intern("read_map_begin");
read_map_end_method_id = rb_intern("read_map_end");
read_list_begin_method_id = rb_intern("read_list_begin");
read_list_end_method_id = rb_intern("read_list_end");
read_set_begin_method_id = rb_intern("read_set_begin");
read_set_end_method_id = rb_intern("read_set_end");
read_struct_begin_method_id = rb_intern("read_struct_begin");
read_struct_end_method_id = rb_intern("read_struct_end");
read_field_begin_method_id = rb_intern("read_field_begin");
read_field_end_method_id = rb_intern("read_field_end");
keys_method_id = rb_intern("keys");
entries_method_id = rb_intern("entries");
write_field_stop_method_id = rb_intern("write_field_stop");
skip_method_id = rb_intern("skip");
write_method_id = rb_intern("write");
read_all_method_id = rb_intern("read_all");
read_into_buffer_method_id = rb_intern("read_into_buffer");
force_binary_encoding_id = rb_intern("force_binary_encoding");
convert_to_utf8_byte_buffer_id = rb_intern("convert_to_utf8_byte_buffer");
convert_to_string_id = rb_intern("convert_to_string");
// constant ids
fields_const_id = rb_intern("FIELDS");
transport_ivar_id = rb_intern("@trans");
strict_read_ivar_id = rb_intern("@strict_read");
strict_write_ivar_id = rb_intern("@strict_write");
// cached symbols
type_sym = ID2SYM(rb_intern("type"));
name_sym = ID2SYM(rb_intern("name"));
key_sym = ID2SYM(rb_intern("key"));
value_sym = ID2SYM(rb_intern("value"));
element_sym = ID2SYM(rb_intern("element"));
class_sym = ID2SYM(rb_intern("class"));
binary_sym = ID2SYM(rb_intern("binary"));
Init_struct();
Init_binary_protocol_accelerated();
Init_compact_protocol();
Init_memory_buffer();
}

View file

@ -0,0 +1,70 @@
#
# 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.
#
# Contains some contributions under the Thrift Software License.
# Please see doc/old-thrift-license.txt in the Thrift distribution for
# details.
$:.unshift File.dirname(__FILE__)
require 'thrift/bytes'
require 'thrift/core_ext'
require 'thrift/exceptions'
require 'thrift/types'
require 'thrift/processor'
require 'thrift/multiplexed_processor'
require 'thrift/client'
require 'thrift/struct'
require 'thrift/union'
require 'thrift/struct_union'
# serializer
require 'thrift/serializer/serializer'
require 'thrift/serializer/deserializer'
# protocol
require 'thrift/protocol/base_protocol'
require 'thrift/protocol/binary_protocol'
require 'thrift/protocol/binary_protocol_accelerated'
require 'thrift/protocol/compact_protocol'
require 'thrift/protocol/json_protocol'
require 'thrift/protocol/multiplexed_protocol'
# transport
require 'thrift/transport/base_transport'
require 'thrift/transport/base_server_transport'
require 'thrift/transport/socket'
require 'thrift/transport/ssl_socket'
require 'thrift/transport/server_socket'
require 'thrift/transport/ssl_server_socket'
require 'thrift/transport/unix_socket'
require 'thrift/transport/unix_server_socket'
require 'thrift/transport/buffered_transport'
require 'thrift/transport/framed_transport'
require 'thrift/transport/http_client_transport'
require 'thrift/transport/io_stream_transport'
require 'thrift/transport/memory_buffer_transport'
# server
require 'thrift/server/base_server'
require 'thrift/server/nonblocking_server'
require 'thrift/server/simple_server'
require 'thrift/server/threaded_server'
require 'thrift/server/thread_pool_server'
require 'thrift/thrift_native'

View file

@ -0,0 +1,131 @@
# encoding: ascii-8bit
#
# 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.
#
module Thrift
# A collection of utilities for working with bytes and byte buffers.
module Bytes
if RUBY_VERSION >= '1.9'
# Creates and empty byte buffer (String with BINARY encoding)
#
# size - The Integer size of the buffer (default: nil) to create
#
# Returns a String with BINARY encoding, filled with null characters
# if size is greater than zero
def self.empty_byte_buffer(size = nil)
if (size && size > 0)
"\0".force_encoding(Encoding::BINARY) * size
else
''.force_encoding(Encoding::BINARY)
end
end
# Forces the encoding of the buffer to BINARY. If the buffer
# passed is frozen, then it will be duplicated.
#
# buffer - The String to force the encoding of.
#
# Returns the String passed with an encoding of BINARY; returned
# String may be a duplicate.
def self.force_binary_encoding(buffer)
buffer = buffer.dup if buffer.frozen?
buffer.force_encoding(Encoding::BINARY)
end
# Gets the byte value of a given position in a String.
#
# string - The String to retrive the byte value from.
# index - The Integer location of the byte value to retrieve.
#
# Returns an Integer value between 0 and 255.
def self.get_string_byte(string, index)
string.getbyte(index)
end
# Sets the byte value given to a given index in a String.
#
# string - The String to set the byte value in.
# index - The Integer location to set the byte value at.
# byte - The Integer value (0 to 255) to set in the string.
#
# Returns an Integer value of the byte value to set.
def self.set_string_byte(string, index, byte)
string.setbyte(index, byte)
end
# Converts the given String to a UTF-8 byte buffer.
#
# string - The String to convert.
#
# Returns a new String with BINARY encoding, containing the UTF-8
# bytes of the original string.
def self.convert_to_utf8_byte_buffer(string)
if string.encoding != Encoding::UTF_8
# transcode to UTF-8
string = string.encode(Encoding::UTF_8)
else
# encoding is already UTF-8, but a duplicate is needed
string = string.dup
end
string.force_encoding(Encoding::BINARY)
end
# Converts the given UTF-8 byte buffer into a String
#
# utf8_buffer - A String, with BINARY encoding, containing UTF-8 bytes
#
# Returns a new String with UTF-8 encoding,
def self.convert_to_string(utf8_buffer)
# duplicate the buffer, force encoding to UTF-8
utf8_buffer.dup.force_encoding(Encoding::UTF_8)
end
else
def self.empty_byte_buffer(size = nil)
if (size && size > 0)
"\0" * size
else
''
end
end
def self.force_binary_encoding(buffer)
buffer
end
def self.get_string_byte(string, index)
string[index]
end
def self.set_string_byte(string, index, byte)
string[index] = byte
end
def self.convert_to_utf8_byte_buffer(string)
# This assumes $KCODE is 'UTF8'/'U', which would mean the String is already a UTF-8 byte buffer
# TODO consider handling other $KCODE values and transcoding with iconv
string
end
def self.convert_to_string(utf8_buffer)
# See comment in 'convert_to_utf8_byte_buffer' for relevant assumptions.
utf8_buffer
end
end
end
end

View file

@ -0,0 +1,71 @@
#
# 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.
#
module Thrift
module Client
def initialize(iprot, oprot=nil)
@iprot = iprot
@oprot = oprot || iprot
@seqid = 0
end
def send_message(name, args_class, args = {})
@oprot.write_message_begin(name, MessageTypes::CALL, @seqid)
send_message_args(args_class, args)
end
def send_oneway_message(name, args_class, args = {})
@oprot.write_message_begin(name, MessageTypes::ONEWAY, @seqid)
send_message_args(args_class, args)
end
def send_message_args(args_class, args)
data = args_class.new
args.each do |k, v|
data.send("#{k.to_s}=", v)
end
begin
data.write(@oprot)
rescue StandardError => e
@oprot.trans.close
raise e
end
@oprot.write_message_end
@oprot.trans.flush
end
def receive_message(result_klass)
fname, mtype, rseqid = @iprot.read_message_begin
handle_exception(mtype)
result = result_klass.new
result.read(@iprot)
@iprot.read_message_end
result
end
def handle_exception(mtype)
if mtype == MessageTypes::EXCEPTION
x = ApplicationException.new
x.read(@iprot)
@iprot.read_message_end
raise x
end
end
end
end

View file

@ -0,0 +1,23 @@
#
# 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.
#
Dir[File.dirname(__FILE__) + "/core_ext/*.rb"].each do |file|
name = File.basename(file, '.rb')
require "thrift/core_ext/#{name}"
end

View file

@ -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.
#
# Versions of ruby pre 1.8.7 do not have an .ord method available in the Fixnum
# class.
#
if RUBY_VERSION < "1.8.7"
class Fixnum
def ord
self
end
end
end

View file

@ -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.
#
module Thrift
class Exception < StandardError
def initialize(message)
super
@message = message
end
attr_reader :message
end
class ApplicationException < Exception
UNKNOWN = 0
UNKNOWN_METHOD = 1
INVALID_MESSAGE_TYPE = 2
WRONG_METHOD_NAME = 3
BAD_SEQUENCE_ID = 4
MISSING_RESULT = 5
INTERNAL_ERROR = 6
PROTOCOL_ERROR = 7
INVALID_TRANSFORM = 8
INVALID_PROTOCOL = 9
UNSUPPORTED_CLIENT_TYPE = 10
attr_reader :type
def initialize(type=UNKNOWN, message=nil)
super(message)
@type = type
end
def read(iprot)
iprot.read_struct_begin
while true
fname, ftype, fid = iprot.read_field_begin
if ftype == Types::STOP
break
end
if fid == 1 and ftype == Types::STRING
@message = iprot.read_string
elsif fid == 2 and ftype == Types::I32
@type = iprot.read_i32
else
iprot.skip(ftype)
end
iprot.read_field_end
end
iprot.read_struct_end
end
def write(oprot)
oprot.write_struct_begin('Thrift::ApplicationException')
unless @message.nil?
oprot.write_field_begin('message', Types::STRING, 1)
oprot.write_string(@message)
oprot.write_field_end
end
unless @type.nil?
oprot.write_field_begin('type', Types::I32, 2)
oprot.write_i32(@type)
oprot.write_field_end
end
oprot.write_field_stop
oprot.write_struct_end
end
end
end

View file

@ -0,0 +1,76 @@
#
# 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.
require 'thrift/protocol/protocol_decorator'
require 'thrift/protocol/base_protocol'
module Thrift
class MultiplexedProcessor
def initialize
@actual_processors = {}
end
def register_processor(service_name, processor)
@actual_processors[service_name] = processor
end
def process(iprot, oprot)
name, type, seqid = iprot.read_message_begin
check_type(type)
check_separator(name)
service_name, method = name.split(':')
processor(service_name).process(StoredMessageProtocol.new(iprot, [method, type, seqid]), oprot)
end
protected
def processor(service_name)
if @actual_processors.has_key?(service_name)
@actual_processors[service_name]
else
raise Thrift::Exception.new("Service name not found: #{service_name}. Did you forget to call #{self.class.name}#register_processor?")
end
end
def check_type(type)
unless [MessageTypes::CALL, MessageTypes::ONEWAY].include?(type)
raise Thrift::Exception.new('This should not have happened!?')
end
end
def check_separator(name)
if name.count(':') < 1
raise Thrift::Exception.new("Service name not found in message name: #{name}. Did you forget to use a Thrift::Protocol::MultiplexedProtocol in your client?")
end
end
end
class StoredMessageProtocol < BaseProtocol
include ProtocolDecorator
def initialize(protocol, message_begin)
super(protocol)
@message_begin = message_begin
end
def read_message_begin
@message_begin
end
end
end

View file

@ -0,0 +1,75 @@
#
# 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.
#
require 'logger'
module Thrift
module Processor
def initialize(handler, logger=nil)
@handler = handler
if logger.nil?
@logger = Logger.new(STDERR)
@logger.level = Logger::WARN
else
@logger = logger
end
end
def process(iprot, oprot)
name, type, seqid = iprot.read_message_begin
if respond_to?("process_#{name}")
begin
send("process_#{name}", seqid, iprot, oprot)
rescue => e
x = ApplicationException.new(ApplicationException::INTERNAL_ERROR, 'Internal error')
@logger.debug "Internal error : #{e.message}\n#{e.backtrace.join("\n")}"
write_error(x, oprot, name, seqid)
end
true
else
iprot.skip(Types::STRUCT)
iprot.read_message_end
x = ApplicationException.new(ApplicationException::UNKNOWN_METHOD, 'Unknown function '+name)
write_error(x, oprot, name, seqid)
false
end
end
def read_args(iprot, args_class)
args = args_class.new
args.read(iprot)
iprot.read_message_end
args
end
def write_result(result, oprot, name, seqid)
oprot.write_message_begin(name, MessageTypes::REPLY, seqid)
result.write(oprot)
oprot.write_message_end
oprot.trans.flush
end
def write_error(err, oprot, name, seqid)
oprot.write_message_begin(name, MessageTypes::EXCEPTION, seqid)
err.write(oprot)
oprot.write_message_end
oprot.trans.flush
end
end
end

View file

@ -0,0 +1,379 @@
#
# 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.
#
# this require is to make generated struct definitions happy
require 'set'
module Thrift
class ProtocolException < Exception
UNKNOWN = 0
INVALID_DATA = 1
NEGATIVE_SIZE = 2
SIZE_LIMIT = 3
BAD_VERSION = 4
NOT_IMPLEMENTED = 5
DEPTH_LIMIT = 6
attr_reader :type
def initialize(type=UNKNOWN, message=nil)
super(message)
@type = type
end
end
class BaseProtocol
attr_reader :trans
def initialize(trans)
@trans = trans
end
def native?
puts "wrong method is being called!"
false
end
def write_message_begin(name, type, seqid)
raise NotImplementedError
end
def write_message_end; nil; end
def write_struct_begin(name)
raise NotImplementedError
end
def write_struct_end; nil; end
def write_field_begin(name, type, id)
raise NotImplementedError
end
def write_field_end; nil; end
def write_field_stop
raise NotImplementedError
end
def write_map_begin(ktype, vtype, size)
raise NotImplementedError
end
def write_map_end; nil; end
def write_list_begin(etype, size)
raise NotImplementedError
end
def write_list_end; nil; end
def write_set_begin(etype, size)
raise NotImplementedError
end
def write_set_end; nil; end
def write_bool(bool)
raise NotImplementedError
end
def write_byte(byte)
raise NotImplementedError
end
def write_i16(i16)
raise NotImplementedError
end
def write_i32(i32)
raise NotImplementedError
end
def write_i64(i64)
raise NotImplementedError
end
def write_double(dub)
raise NotImplementedError
end
# Writes a Thrift String. In Ruby 1.9+, the String passed will be transcoded to UTF-8.
#
# str - The String to write.
#
# Raises EncodingError if the transcoding to UTF-8 fails.
#
# Returns nothing.
def write_string(str)
raise NotImplementedError
end
# Writes a Thrift Binary (Thrift String with no encoding). In Ruby 1.9+, the String passed
# will forced into BINARY encoding.
#
# buf - The String to write.
#
# Returns nothing.
def write_binary(buf)
raise NotImplementedError
end
def read_message_begin
raise NotImplementedError
end
def read_message_end; nil; end
def read_struct_begin
raise NotImplementedError
end
def read_struct_end; nil; end
def read_field_begin
raise NotImplementedError
end
def read_field_end; nil; end
def read_map_begin
raise NotImplementedError
end
def read_map_end; nil; end
def read_list_begin
raise NotImplementedError
end
def read_list_end; nil; end
def read_set_begin
raise NotImplementedError
end
def read_set_end; nil; end
def read_bool
raise NotImplementedError
end
def read_byte
raise NotImplementedError
end
def read_i16
raise NotImplementedError
end
def read_i32
raise NotImplementedError
end
def read_i64
raise NotImplementedError
end
def read_double
raise NotImplementedError
end
# Reads a Thrift String. In Ruby 1.9+, all Strings will be returned with an Encoding of UTF-8.
#
# Returns a String.
def read_string
raise NotImplementedError
end
# Reads a Thrift Binary (Thrift String without encoding). In Ruby 1.9+, all Strings will be returned
# with an Encoding of BINARY.
#
# Returns a String.
def read_binary
raise NotImplementedError
end
# Writes a field based on the field information, field ID and value.
#
# field_info - A Hash containing the definition of the field:
# :name - The name of the field.
# :type - The type of the field, which must be a Thrift::Types constant.
# :binary - A Boolean flag that indicates if Thrift::Types::STRING is a binary string (string without encoding).
# fid - The ID of the field.
# value - The field's value to write; object type varies based on :type.
#
# Returns nothing.
def write_field(*args)
if args.size == 3
# handles the documented method signature - write_field(field_info, fid, value)
field_info = args[0]
fid = args[1]
value = args[2]
elsif args.size == 4
# handles the deprecated method signature - write_field(name, type, fid, value)
field_info = {:name => args[0], :type => args[1]}
fid = args[2]
value = args[3]
else
raise ArgumentError, "wrong number of arguments (#{args.size} for 3)"
end
write_field_begin(field_info[:name], field_info[:type], fid)
write_type(field_info, value)
write_field_end
end
# Writes a field value based on the field information.
#
# field_info - A Hash containing the definition of the field:
# :type - The Thrift::Types constant that determines how the value is written.
# :binary - A Boolean flag that indicates if Thrift::Types::STRING is a binary string (string without encoding).
# value - The field's value to write; object type varies based on field_info[:type].
#
# Returns nothing.
def write_type(field_info, value)
# if field_info is a Fixnum, assume it is a Thrift::Types constant
# convert it into a field_info Hash for backwards compatibility
if field_info.is_a? Fixnum
field_info = {:type => field_info}
end
case field_info[:type]
when Types::BOOL
write_bool(value)
when Types::BYTE
write_byte(value)
when Types::DOUBLE
write_double(value)
when Types::I16
write_i16(value)
when Types::I32
write_i32(value)
when Types::I64
write_i64(value)
when Types::STRING
if field_info[:binary]
write_binary(value)
else
write_string(value)
end
when Types::STRUCT
value.write(self)
else
raise NotImplementedError
end
end
# Reads a field value based on the field information.
#
# field_info - A Hash containing the pertinent data to write:
# :type - The Thrift::Types constant that determines how the value is written.
# :binary - A flag that indicates if Thrift::Types::STRING is a binary string (string without encoding).
#
# Returns the value read; object type varies based on field_info[:type].
def read_type(field_info)
# if field_info is a Fixnum, assume it is a Thrift::Types constant
# convert it into a field_info Hash for backwards compatibility
if field_info.is_a? Fixnum
field_info = {:type => field_info}
end
case field_info[:type]
when Types::BOOL
read_bool
when Types::BYTE
read_byte
when Types::DOUBLE
read_double
when Types::I16
read_i16
when Types::I32
read_i32
when Types::I64
read_i64
when Types::STRING
if field_info[:binary]
read_binary
else
read_string
end
else
raise NotImplementedError
end
end
def skip(type)
case type
when Types::STOP
nil
when Types::BOOL
read_bool
when Types::BYTE
read_byte
when Types::I16
read_i16
when Types::I32
read_i32
when Types::I64
read_i64
when Types::DOUBLE
read_double
when Types::STRING
read_string
when Types::STRUCT
read_struct_begin
while true
name, type, id = read_field_begin
break if type == Types::STOP
skip(type)
read_field_end
end
read_struct_end
when Types::MAP
ktype, vtype, size = read_map_begin
size.times do
skip(ktype)
skip(vtype)
end
read_map_end
when Types::SET
etype, size = read_set_begin
size.times do
skip(etype)
end
read_set_end
when Types::LIST
etype, size = read_list_begin
size.times do
skip(etype)
end
read_list_end
end
end
end
class BaseProtocolFactory
def get_protocol(trans)
raise NotImplementedError
end
end
end

View file

@ -0,0 +1,237 @@
#
# 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.
#
module Thrift
class BinaryProtocol < BaseProtocol
VERSION_MASK = 0xffff0000
VERSION_1 = 0x80010000
TYPE_MASK = 0x000000ff
attr_reader :strict_read, :strict_write
def initialize(trans, strict_read=true, strict_write=true)
super(trans)
@strict_read = strict_read
@strict_write = strict_write
# Pre-allocated read buffer for fixed-size read methods. Needs to be at least 8 bytes long for
# read_i64() and read_double().
@rbuf = Bytes.empty_byte_buffer(8)
end
def write_message_begin(name, type, seqid)
# this is necessary because we added (needed) bounds checking to
# write_i32, and 0x80010000 is too big for that.
if strict_write
write_i16(VERSION_1 >> 16)
write_i16(type)
write_string(name)
write_i32(seqid)
else
write_string(name)
write_byte(type)
write_i32(seqid)
end
end
def write_struct_begin(name); nil; end
def write_field_begin(name, type, id)
write_byte(type)
write_i16(id)
end
def write_field_stop
write_byte(Thrift::Types::STOP)
end
def write_map_begin(ktype, vtype, size)
write_byte(ktype)
write_byte(vtype)
write_i32(size)
end
def write_list_begin(etype, size)
write_byte(etype)
write_i32(size)
end
def write_set_begin(etype, size)
write_byte(etype)
write_i32(size)
end
def write_bool(bool)
write_byte(bool ? 1 : 0)
end
def write_byte(byte)
raise RangeError if byte < -2**31 || byte >= 2**32
trans.write([byte].pack('c'))
end
def write_i16(i16)
trans.write([i16].pack('n'))
end
def write_i32(i32)
raise RangeError if i32 < -2**31 || i32 >= 2**31
trans.write([i32].pack('N'))
end
def write_i64(i64)
raise RangeError if i64 < -2**63 || i64 >= 2**64
hi = i64 >> 32
lo = i64 & 0xffffffff
trans.write([hi, lo].pack('N2'))
end
def write_double(dub)
trans.write([dub].pack('G'))
end
def write_string(str)
buf = Bytes.convert_to_utf8_byte_buffer(str)
write_binary(buf)
end
def write_binary(buf)
write_i32(buf.bytesize)
trans.write(buf)
end
def read_message_begin
version = read_i32
if version < 0
if (version & VERSION_MASK != VERSION_1)
raise ProtocolException.new(ProtocolException::BAD_VERSION, 'Missing version identifier')
end
type = version & TYPE_MASK
name = read_string
seqid = read_i32
[name, type, seqid]
else
if strict_read
raise ProtocolException.new(ProtocolException::BAD_VERSION, 'No version identifier, old protocol client?')
end
name = trans.read_all(version)
type = read_byte
seqid = read_i32
[name, type, seqid]
end
end
def read_struct_begin; nil; end
def read_field_begin
type = read_byte
if (type == Types::STOP)
[nil, type, 0]
else
id = read_i16
[nil, type, id]
end
end
def read_map_begin
ktype = read_byte
vtype = read_byte
size = read_i32
[ktype, vtype, size]
end
def read_list_begin
etype = read_byte
size = read_i32
[etype, size]
end
def read_set_begin
etype = read_byte
size = read_i32
[etype, size]
end
def read_bool
byte = read_byte
byte != 0
end
def read_byte
val = trans.read_byte
if (val > 0x7f)
val = 0 - ((val - 1) ^ 0xff)
end
val
end
def read_i16
trans.read_into_buffer(@rbuf, 2)
val, = @rbuf.unpack('n')
if (val > 0x7fff)
val = 0 - ((val - 1) ^ 0xffff)
end
val
end
def read_i32
trans.read_into_buffer(@rbuf, 4)
val, = @rbuf.unpack('N')
if (val > 0x7fffffff)
val = 0 - ((val - 1) ^ 0xffffffff)
end
val
end
def read_i64
trans.read_into_buffer(@rbuf, 8)
hi, lo = @rbuf.unpack('N2')
if (hi > 0x7fffffff)
hi ^= 0xffffffff
lo ^= 0xffffffff
0 - (hi << 32) - lo - 1
else
(hi << 32) + lo
end
end
def read_double
trans.read_into_buffer(@rbuf, 8)
val = @rbuf.unpack('G').first
val
end
def read_string
buffer = read_binary
Bytes.convert_to_string(buffer)
end
def read_binary
size = read_i32
trans.read_all(size)
end
end
class BinaryProtocolFactory < BaseProtocolFactory
def get_protocol(trans)
return Thrift::BinaryProtocol.new(trans)
end
end
end

View file

@ -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.
#
=begin
The only change required for a transport to support BinaryProtocolAccelerated is to implement 2 methods:
* borrow(size), which takes an optional argument and returns atleast _size_ bytes from the transport,
or the default buffer size if no argument is given
* consume!(size), which removes size bytes from the front of the buffer
See MemoryBuffer and BufferedTransport for examples.
=end
module Thrift
class BinaryProtocolAcceleratedFactory < BaseProtocolFactory
def get_protocol(trans)
if (defined? BinaryProtocolAccelerated)
BinaryProtocolAccelerated.new(trans)
else
BinaryProtocol.new(trans)
end
end
end
end

View file

@ -0,0 +1,435 @@
#
# 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.
#
module Thrift
class CompactProtocol < BaseProtocol
PROTOCOL_ID = [0x82].pack('c').unpack('c').first
VERSION = 1
VERSION_MASK = 0x1f
TYPE_MASK = 0xE0
TYPE_BITS = 0x07
TYPE_SHIFT_AMOUNT = 5
TSTOP = ["", Types::STOP, 0]
#
# All of the on-wire type codes.
#
class CompactTypes
BOOLEAN_TRUE = 0x01
BOOLEAN_FALSE = 0x02
BYTE = 0x03
I16 = 0x04
I32 = 0x05
I64 = 0x06
DOUBLE = 0x07
BINARY = 0x08
LIST = 0x09
SET = 0x0A
MAP = 0x0B
STRUCT = 0x0C
def self.is_bool_type?(b)
(b & 0x0f) == BOOLEAN_TRUE || (b & 0x0f) == BOOLEAN_FALSE
end
COMPACT_TO_TTYPE = {
Types::STOP => Types::STOP,
BOOLEAN_FALSE => Types::BOOL,
BOOLEAN_TRUE => Types::BOOL,
BYTE => Types::BYTE,
I16 => Types::I16,
I32 => Types::I32,
I64 => Types::I64,
DOUBLE => Types::DOUBLE,
BINARY => Types::STRING,
LIST => Types::LIST,
SET => Types::SET,
MAP => Types::MAP,
STRUCT => Types::STRUCT
}
TTYPE_TO_COMPACT = {
Types::STOP => Types::STOP,
Types::BOOL => BOOLEAN_TRUE,
Types::BYTE => BYTE,
Types::I16 => I16,
Types::I32 => I32,
Types::I64 => I64,
Types::DOUBLE => DOUBLE,
Types::STRING => BINARY,
Types::LIST => LIST,
Types::SET => SET,
Types::MAP => MAP,
Types::STRUCT => STRUCT
}
def self.get_ttype(compact_type)
val = COMPACT_TO_TTYPE[compact_type & 0x0f]
raise "don't know what type: #{compact_type & 0x0f}" unless val
val
end
def self.get_compact_type(ttype)
val = TTYPE_TO_COMPACT[ttype]
raise "don't know what type: #{ttype & 0x0f}" unless val
val
end
end
def initialize(transport)
super(transport)
@last_field = [0]
@boolean_value = nil
# Pre-allocated read buffer for read_double().
@rbuf = Bytes.empty_byte_buffer(8)
end
def write_message_begin(name, type, seqid)
write_byte(PROTOCOL_ID)
write_byte((VERSION & VERSION_MASK) | ((type << TYPE_SHIFT_AMOUNT) & TYPE_MASK))
write_varint32(seqid)
write_string(name)
nil
end
def write_struct_begin(name)
@last_field.push(0)
nil
end
def write_struct_end
@last_field.pop
nil
end
def write_field_begin(name, type, id)
if type == Types::BOOL
# we want to possibly include the value, so we'll wait.
@boolean_field = [type, id]
else
write_field_begin_internal(type, id)
end
nil
end
#
# The workhorse of writeFieldBegin. It has the option of doing a
# 'type override' of the type header. This is used specifically in the
# boolean field case.
#
def write_field_begin_internal(type, id, type_override=nil)
last_id = @last_field.pop
# if there's a type override, use that.
typeToWrite = type_override || CompactTypes.get_compact_type(type)
# check if we can use delta encoding for the field id
if id > last_id && id - last_id <= 15
# write them together
write_byte((id - last_id) << 4 | typeToWrite)
else
# write them separate
write_byte(typeToWrite)
write_i16(id)
end
@last_field.push(id)
nil
end
def write_field_stop
write_byte(Types::STOP)
end
def write_map_begin(ktype, vtype, size)
if (size == 0)
write_byte(0)
else
write_varint32(size)
write_byte(CompactTypes.get_compact_type(ktype) << 4 | CompactTypes.get_compact_type(vtype))
end
end
def write_list_begin(etype, size)
write_collection_begin(etype, size)
end
def write_set_begin(etype, size)
write_collection_begin(etype, size);
end
def write_bool(bool)
type = bool ? CompactTypes::BOOLEAN_TRUE : CompactTypes::BOOLEAN_FALSE
unless @boolean_field.nil?
# we haven't written the field header yet
write_field_begin_internal(@boolean_field.first, @boolean_field.last, type)
@boolean_field = nil
else
# we're not part of a field, so just write the value.
write_byte(type)
end
end
def write_byte(byte)
@trans.write([byte].pack('c'))
end
def write_i16(i16)
write_varint32(int_to_zig_zag(i16))
end
def write_i32(i32)
write_varint32(int_to_zig_zag(i32))
end
def write_i64(i64)
write_varint64(long_to_zig_zag(i64))
end
def write_double(dub)
@trans.write([dub].pack("G").reverse)
end
def write_string(str)
buf = Bytes.convert_to_utf8_byte_buffer(str)
write_binary(buf)
end
def write_binary(buf)
write_varint32(buf.bytesize)
@trans.write(buf)
end
def read_message_begin
protocol_id = read_byte()
if protocol_id != PROTOCOL_ID
raise ProtocolException.new("Expected protocol id #{PROTOCOL_ID} but got #{protocol_id}")
end
version_and_type = read_byte()
version = version_and_type & VERSION_MASK
if (version != VERSION)
raise ProtocolException.new("Expected version #{VERSION} but got #{version}");
end
type = (version_and_type >> TYPE_SHIFT_AMOUNT) & TYPE_BITS
seqid = read_varint32()
messageName = read_string()
[messageName, type, seqid]
end
def read_struct_begin
@last_field.push(0)
""
end
def read_struct_end
@last_field.pop()
nil
end
def read_field_begin
type = read_byte()
# if it's a stop, then we can return immediately, as the struct is over.
if (type & 0x0f) == Types::STOP
TSTOP
else
field_id = nil
# mask off the 4 MSB of the type header. it could contain a field id delta.
modifier = (type & 0xf0) >> 4
if modifier == 0
# not a delta. look ahead for the zigzag varint field id.
@last_field.pop
field_id = read_i16()
else
# has a delta. add the delta to the last read field id.
field_id = @last_field.pop + modifier
end
# if this happens to be a boolean field, the value is encoded in the type
if CompactTypes.is_bool_type?(type)
# save the boolean value in a special instance variable.
@bool_value = (type & 0x0f) == CompactTypes::BOOLEAN_TRUE
end
# push the new field onto the field stack so we can keep the deltas going.
@last_field.push(field_id)
["", CompactTypes.get_ttype(type & 0x0f), field_id]
end
end
def read_map_begin
size = read_varint32()
key_and_value_type = size == 0 ? 0 : read_byte()
[CompactTypes.get_ttype(key_and_value_type >> 4), CompactTypes.get_ttype(key_and_value_type & 0xf), size]
end
def read_list_begin
size_and_type = read_byte()
size = (size_and_type >> 4) & 0x0f
if size == 15
size = read_varint32()
end
type = CompactTypes.get_ttype(size_and_type)
[type, size]
end
def read_set_begin
read_list_begin
end
def read_bool
unless @bool_value.nil?
bv = @bool_value
@bool_value = nil
bv
else
read_byte() == CompactTypes::BOOLEAN_TRUE
end
end
def read_byte
val = trans.read_byte
if (val > 0x7f)
val = 0 - ((val - 1) ^ 0xff)
end
val
end
def read_i16
zig_zag_to_int(read_varint32())
end
def read_i32
zig_zag_to_int(read_varint32())
end
def read_i64
zig_zag_to_long(read_varint64())
end
def read_double
trans.read_into_buffer(@rbuf, 8)
val = @rbuf.reverse.unpack('G').first
val
end
def read_string
buffer = read_binary
Bytes.convert_to_string(buffer)
end
def read_binary
size = read_varint32()
trans.read_all(size)
end
private
#
# Abstract method for writing the start of lists and sets. List and sets on
# the wire differ only by the type indicator.
#
def write_collection_begin(elem_type, size)
if size <= 14
write_byte(size << 4 | CompactTypes.get_compact_type(elem_type))
else
write_byte(0xf0 | CompactTypes.get_compact_type(elem_type))
write_varint32(size)
end
end
def write_varint32(n)
# int idx = 0;
while true
if (n & ~0x7F) == 0
# i32buf[idx++] = (byte)n;
write_byte(n)
break
# return;
else
# i32buf[idx++] = (byte)((n & 0x7F) | 0x80);
write_byte((n & 0x7F) | 0x80)
n = n >> 7
end
end
# trans_.write(i32buf, 0, idx);
end
SEVEN_BIT_MASK = 0x7F
EVERYTHING_ELSE_MASK = ~SEVEN_BIT_MASK
def write_varint64(n)
while true
if (n & EVERYTHING_ELSE_MASK) == 0 #TODO need to find a way to make this into a long...
write_byte(n)
break
else
write_byte((n & SEVEN_BIT_MASK) | 0x80)
n >>= 7
end
end
end
def read_varint32()
read_varint64()
end
def read_varint64()
shift = 0
result = 0
while true
b = read_byte()
result |= (b & 0x7f) << shift
break if (b & 0x80) != 0x80
shift += 7
end
result
end
def int_to_zig_zag(n)
(n << 1) ^ (n >> 31)
end
def long_to_zig_zag(l)
# puts "zz encoded #{l} to #{(l << 1) ^ (l >> 63)}"
(l << 1) ^ (l >> 63)
end
def zig_zag_to_int(n)
(n >> 1) ^ -(n & 1)
end
def zig_zag_to_long(n)
(n >> 1) ^ -(n & 1)
end
end
class CompactProtocolFactory < BaseProtocolFactory
def get_protocol(trans)
CompactProtocol.new(trans)
end
end
end

View file

@ -0,0 +1,778 @@
# encoding: UTF-8
#
# 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.
#
require 'base64'
module Thrift
class LookaheadReader
def initialize(trans)
@trans = trans
@hasData = false
@data = nil
end
def read
if @hasData
@hasData = false
else
@data = @trans.read(1)
end
return @data
end
def peek
if !@hasData
@data = @trans.read(1)
end
@hasData = true
return @data
end
end
#
# Class to serve as base JSON context and as base class for other context
# implementations
#
class JSONContext
@@kJSONElemSeparator = ','
#
# Write context data to the trans. Default is to do nothing.
#
def write(trans)
end
#
# Read context data from the trans. Default is to do nothing.
#
def read(reader)
end
#
# Return true if numbers need to be escaped as strings in this context.
# Default behavior is to return false.
#
def escapeNum
return false
end
end
# Context class for object member key-value pairs
class JSONPairContext < JSONContext
@@kJSONPairSeparator = ':'
def initialize
@first = true
@colon = true
end
def write(trans)
if (@first)
@first = false
@colon = true
else
trans.write(@colon ? @@kJSONPairSeparator : @@kJSONElemSeparator)
@colon = !@colon
end
end
def read(reader)
if (@first)
@first = false
@colon = true
else
ch = (@colon ? @@kJSONPairSeparator : @@kJSONElemSeparator)
@colon = !@colon
JsonProtocol::read_syntax_char(reader, ch)
end
end
# Numbers must be turned into strings if they are the key part of a pair
def escapeNum
return @colon
end
end
# Context class for lists
class JSONListContext < JSONContext
def initialize
@first = true
end
def write(trans)
if (@first)
@first = false
else
trans.write(@@kJSONElemSeparator)
end
end
def read(reader)
if (@first)
@first = false
else
JsonProtocol::read_syntax_char(reader, @@kJSONElemSeparator)
end
end
end
class JsonProtocol < BaseProtocol
@@kJSONObjectStart = '{'
@@kJSONObjectEnd = '}'
@@kJSONArrayStart = '['
@@kJSONArrayEnd = ']'
@@kJSONNewline = '\n'
@@kJSONBackslash = '\\'
@@kJSONStringDelimiter = '"'
@@kThriftVersion1 = 1
@@kThriftNan = "NaN"
@@kThriftInfinity = "Infinity"
@@kThriftNegativeInfinity = "-Infinity"
def initialize(trans)
super(trans)
@context = JSONContext.new
@contexts = Array.new
@reader = LookaheadReader.new(trans)
end
def get_type_name_for_type_id(id)
case id
when Types::BOOL
"tf"
when Types::BYTE
"i8"
when Types::I16
"i16"
when Types::I32
"i32"
when Types::I64
"i64"
when Types::DOUBLE
"dbl"
when Types::STRING
"str"
when Types::STRUCT
"rec"
when Types::MAP
"map"
when Types::SET
"set"
when Types::LIST
"lst"
else
raise NotImplementedError
end
end
def get_type_id_for_type_name(name)
if (name == "tf")
result = Types::BOOL
elsif (name == "i8")
result = Types::BYTE
elsif (name == "i16")
result = Types::I16
elsif (name == "i32")
result = Types::I32
elsif (name == "i64")
result = Types::I64
elsif (name == "dbl")
result = Types::DOUBLE
elsif (name == "str")
result = Types::STRING
elsif (name == "rec")
result = Types::STRUCT
elsif (name == "map")
result = Types::MAP
elsif (name == "set")
result = Types::SET
elsif (name == "lst")
result = Types::LIST
else
result = Types::STOP
end
if (result == Types::STOP)
raise NotImplementedError
end
return result
end
# Static helper functions
# Read 1 character from the trans and verify that it is the expected character ch.
# Throw a protocol exception if it is not.
def self.read_syntax_char(reader, ch)
ch2 = reader.read
if (ch2 != ch)
raise ProtocolException.new(ProtocolException::INVALID_DATA, "Expected \'#{ch}\' got \'#{ch2}\'.")
end
end
# Return true if the character ch is in [-+0-9.Ee]; false otherwise
def is_json_numeric(ch)
case ch
when '+', '-', '.', '0' .. '9', 'E', "e"
return true
else
return false
end
end
def push_context(context)
@contexts.push(@context)
@context = context
end
def pop_context
@context = @contexts.pop
end
# Write the character ch as a JSON escape sequence ("\u00xx")
def write_json_escape_char(ch)
trans.write('\\u')
ch_value = ch[0]
if (ch_value.kind_of? String)
ch_value = ch.bytes.first
end
trans.write(ch_value.to_s(16).rjust(4,'0'))
end
# Write the character ch as part of a JSON string, escaping as appropriate.
def write_json_char(ch)
# This table describes the handling for the first 0x30 characters
# 0 : escape using "\u00xx" notation
# 1 : just output index
# <other> : escape using "\<other>" notation
kJSONCharTable = [
# 0 1 2 3 4 5 6 7 8 9 A B C D E F
0, 0, 0, 0, 0, 0, 0, 0,'b','t','n', 0,'f','r', 0, 0, # 0
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 1
1, 1,'"', 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, # 2
]
ch_value = ch[0]
if (ch_value.kind_of? String)
ch_value = ch.bytes.first
end
if (ch_value >= 0x30)
if (ch == @@kJSONBackslash) # Only special character >= 0x30 is '\'
trans.write(@@kJSONBackslash)
trans.write(@@kJSONBackslash)
else
trans.write(ch)
end
else
outCh = kJSONCharTable[ch_value];
# Check if regular character, backslash escaped, or JSON escaped
if outCh.kind_of? String
trans.write(@@kJSONBackslash)
trans.write(outCh)
elsif outCh == 1
trans.write(ch)
else
write_json_escape_char(ch)
end
end
end
# Write out the contents of the string str as a JSON string, escaping characters as appropriate.
def write_json_string(str)
@context.write(trans)
trans.write(@@kJSONStringDelimiter)
str.split('').each do |ch|
write_json_char(ch)
end
trans.write(@@kJSONStringDelimiter)
end
# Write out the contents of the string as JSON string, base64-encoding
# the string's contents, and escaping as appropriate
def write_json_base64(str)
@context.write(trans)
trans.write(@@kJSONStringDelimiter)
trans.write(Base64.strict_encode64(str))
trans.write(@@kJSONStringDelimiter)
end
# Convert the given integer type to a JSON number, or a string
# if the context requires it (eg: key in a map pair).
def write_json_integer(num)
@context.write(trans)
escapeNum = @context.escapeNum
if (escapeNum)
trans.write(@@kJSONStringDelimiter)
end
trans.write(num.to_s);
if (escapeNum)
trans.write(@@kJSONStringDelimiter)
end
end
# Convert the given double to a JSON string, which is either the number,
# "NaN" or "Infinity" or "-Infinity".
def write_json_double(num)
@context.write(trans)
# Normalize output of thrift::to_string for NaNs and Infinities
special = false;
if (num.nan?)
special = true;
val = @@kThriftNan;
elsif (num.infinite?)
special = true;
val = @@kThriftInfinity;
if (num < 0.0)
val = @@kThriftNegativeInfinity;
end
else
val = num.to_s
end
escapeNum = special || @context.escapeNum
if (escapeNum)
trans.write(@@kJSONStringDelimiter)
end
trans.write(val)
if (escapeNum)
trans.write(@@kJSONStringDelimiter)
end
end
def write_json_object_start
@context.write(trans)
trans.write(@@kJSONObjectStart)
push_context(JSONPairContext.new);
end
def write_json_object_end
pop_context
trans.write(@@kJSONObjectEnd)
end
def write_json_array_start
@context.write(trans)
trans.write(@@kJSONArrayStart)
push_context(JSONListContext.new);
end
def write_json_array_end
pop_context
trans.write(@@kJSONArrayEnd)
end
def write_message_begin(name, type, seqid)
write_json_array_start
write_json_integer(@@kThriftVersion1)
write_json_string(name)
write_json_integer(type)
write_json_integer(seqid)
end
def write_message_end
write_json_array_end
end
def write_struct_begin(name)
write_json_object_start
end
def write_struct_end
write_json_object_end
end
def write_field_begin(name, type, id)
write_json_integer(id)
write_json_object_start
write_json_string(get_type_name_for_type_id(type))
end
def write_field_end
write_json_object_end
end
def write_field_stop; nil; end
def write_map_begin(ktype, vtype, size)
write_json_array_start
write_json_string(get_type_name_for_type_id(ktype))
write_json_string(get_type_name_for_type_id(vtype))
write_json_integer(size)
write_json_object_start
end
def write_map_end
write_json_object_end
write_json_array_end
end
def write_list_begin(etype, size)
write_json_array_start
write_json_string(get_type_name_for_type_id(etype))
write_json_integer(size)
end
def write_list_end
write_json_array_end
end
def write_set_begin(etype, size)
write_json_array_start
write_json_string(get_type_name_for_type_id(etype))
write_json_integer(size)
end
def write_set_end
write_json_array_end
end
def write_bool(bool)
write_json_integer(bool ? 1 : 0)
end
def write_byte(byte)
write_json_integer(byte)
end
def write_i16(i16)
write_json_integer(i16)
end
def write_i32(i32)
write_json_integer(i32)
end
def write_i64(i64)
write_json_integer(i64)
end
def write_double(dub)
write_json_double(dub)
end
def write_string(str)
write_json_string(str)
end
def write_binary(str)
write_json_base64(str)
end
##
# Reading functions
##
# Reads 1 byte and verifies that it matches ch.
def read_json_syntax_char(ch)
JsonProtocol::read_syntax_char(@reader, ch)
end
# Decodes the four hex parts of a JSON escaped string character and returns
# the character via out.
#
# Note - this only supports Unicode characters in the BMP (U+0000 to U+FFFF);
# characters above the BMP are encoded as two escape sequences (surrogate pairs),
# which is not yet implemented
def read_json_escape_char
str = @reader.read
str += @reader.read
str += @reader.read
str += @reader.read
if RUBY_VERSION >= '1.9'
str.hex.chr(Encoding::UTF_8)
else
str.hex.chr
end
end
# Decodes a JSON string, including unescaping, and returns the string via str
def read_json_string(skipContext = false)
# This string's characters must match up with the elements in escape_char_vals.
# I don't have '/' on this list even though it appears on www.json.org --
# it is not in the RFC -> it is. See RFC 4627
escape_chars = "\"\\/bfnrt"
# The elements of this array must match up with the sequence of characters in
# escape_chars
escape_char_vals = [
"\"", "\\", "\/", "\b", "\f", "\n", "\r", "\t",
]
if !skipContext
@context.read(@reader)
end
read_json_syntax_char(@@kJSONStringDelimiter)
ch = ""
str = ""
while (true)
ch = @reader.read
if (ch == @@kJSONStringDelimiter)
break
end
if (ch == @@kJSONBackslash)
ch = @reader.read
if (ch == 'u')
ch = read_json_escape_char
else
pos = escape_chars.index(ch);
if (pos.nil?) # not found
raise ProtocolException.new(ProtocolException::INVALID_DATA, "Expected control char, got \'#{ch}\'.")
end
ch = escape_char_vals[pos]
end
end
str += ch
end
return str
end
# Reads a block of base64 characters, decoding it, and returns via str
def read_json_base64
str = read_json_string
m = str.length % 4
if m != 0
# Add missing padding
(4 - m).times do
str += '='
end
end
Base64.strict_decode64(str)
end
# Reads a sequence of characters, stopping at the first one that is not
# a valid JSON numeric character.
def read_json_numeric_chars
str = ""
while (true)
ch = @reader.peek
if (!is_json_numeric(ch))
break;
end
ch = @reader.read
str += ch
end
return str
end
# Reads a sequence of characters and assembles them into a number,
# returning them via num
def read_json_integer
@context.read(@reader)
if (@context.escapeNum)
read_json_syntax_char(@@kJSONStringDelimiter)
end
str = read_json_numeric_chars
begin
num = Integer(str);
rescue
raise ProtocolException.new(ProtocolException::INVALID_DATA, "Expected numeric value; got \"#{str}\"")
end
if (@context.escapeNum)
read_json_syntax_char(@@kJSONStringDelimiter)
end
return num
end
# Reads a JSON number or string and interprets it as a double.
def read_json_double
@context.read(@reader)
num = 0
if (@reader.peek == @@kJSONStringDelimiter)
str = read_json_string(true)
# Check for NaN, Infinity and -Infinity
if (str == @@kThriftNan)
num = (+1.0/0.0)/(+1.0/0.0)
elsif (str == @@kThriftInfinity)
num = +1.0/0.0
elsif (str == @@kThriftNegativeInfinity)
num = -1.0/0.0
else
if (!@context.escapeNum)
# Raise exception -- we should not be in a string in this case
raise ProtocolException.new(ProtocolException::INVALID_DATA, "Numeric data unexpectedly quoted")
end
begin
num = Float(str)
rescue
raise ProtocolException.new(ProtocolException::INVALID_DATA, "Expected numeric value; got \"#{str}\"")
end
end
else
if (@context.escapeNum)
# This will throw - we should have had a quote if escapeNum == true
read_json_syntax_char(@@kJSONStringDelimiter)
end
str = read_json_numeric_chars
begin
num = Float(str)
rescue
raise ProtocolException.new(ProtocolException::INVALID_DATA, "Expected numeric value; got \"#{str}\"")
end
end
return num
end
def read_json_object_start
@context.read(@reader)
read_json_syntax_char(@@kJSONObjectStart)
push_context(JSONPairContext.new)
nil
end
def read_json_object_end
read_json_syntax_char(@@kJSONObjectEnd)
pop_context
nil
end
def read_json_array_start
@context.read(@reader)
read_json_syntax_char(@@kJSONArrayStart)
push_context(JSONListContext.new)
nil
end
def read_json_array_end
read_json_syntax_char(@@kJSONArrayEnd)
pop_context
nil
end
def read_message_begin
read_json_array_start
version = read_json_integer
if (version != @@kThriftVersion1)
raise ProtocolException.new(ProtocolException::BAD_VERSION, 'Message contained bad version.')
end
name = read_json_string
message_type = read_json_integer
seqid = read_json_integer
[name, message_type, seqid]
end
def read_message_end
read_json_array_end
nil
end
def read_struct_begin
read_json_object_start
nil
end
def read_struct_end
read_json_object_end
nil
end
def read_field_begin
# Check if we hit the end of the list
ch = @reader.peek
if (ch == @@kJSONObjectEnd)
field_type = Types::STOP
else
field_id = read_json_integer
read_json_object_start
field_type = get_type_id_for_type_name(read_json_string)
end
[nil, field_type, field_id]
end
def read_field_end
read_json_object_end
end
def read_map_begin
read_json_array_start
key_type = get_type_id_for_type_name(read_json_string)
val_type = get_type_id_for_type_name(read_json_string)
size = read_json_integer
read_json_object_start
[key_type, val_type, size]
end
def read_map_end
read_json_object_end
read_json_array_end
end
def read_list_begin
read_json_array_start
[get_type_id_for_type_name(read_json_string), read_json_integer]
end
def read_list_end
read_json_array_end
end
def read_set_begin
read_json_array_start
[get_type_id_for_type_name(read_json_string), read_json_integer]
end
def read_set_end
read_json_array_end
end
def read_bool
byte = read_byte
byte != 0
end
def read_byte
read_json_integer
end
def read_i16
read_json_integer
end
def read_i32
read_json_integer
end
def read_i64
read_json_integer
end
def read_double
read_json_double
end
def read_string
read_json_string
end
def read_binary
read_json_base64
end
end
class JsonProtocolFactory < BaseProtocolFactory
def get_protocol(trans)
return Thrift::JsonProtocol.new(trans)
end
end
end

View file

@ -0,0 +1,40 @@
#
# 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.
require 'thrift/protocol/protocol_decorator'
module Thrift
class MultiplexedProtocol < BaseProtocol
include ProtocolDecorator
def initialize(protocol, service_name)
super(protocol)
@service_name = service_name
end
def write_message_begin(name, type, seqid)
case type
when MessageTypes::CALL, MessageTypes::ONEWAY
@protocol.write_message_begin("#{@service_name}:#{name}", type, seqid)
else
@protocol.write_message_begin(name, type, seqid)
end
end
end
end

View file

@ -0,0 +1,194 @@
#
# 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.
module Thrift
module ProtocolDecorator
def initialize(protocol)
@protocol = protocol
end
def trans
@protocol.trans
end
def write_message_begin(name, type, seqid)
@protocol.write_message_begin
end
def write_message_end
@protocol.write_message_end
end
def write_struct_begin(name)
@protocol.write_struct_begin(name)
end
def write_struct_end
@protocol.write_struct_end
end
def write_field_begin(name, type, id)
@protocol.write_field_begin(name, type, id)
end
def write_field_end
@protocol.write_field_end
end
def write_field_stop
@protocol.write_field_stop
end
def write_map_begin(ktype, vtype, size)
@protocol.write_map_begin(ktype, vtype, size)
end
def write_map_end
@protocol.write_map_end
end
def write_list_begin(etype, size)
@protocol.write_list_begin(etype, size)
end
def write_list_end
@protocol.write_list_end
end
def write_set_begin(etype, size)
@protocol.write_set_begin(etype, size)
end
def write_set_end
@protocol.write_set_end
end
def write_bool(bool)
@protocol.write_bool(bool)
end
def write_byte(byte)
@protocol.write_byte(byte)
end
def write_i16(i16)
@protocol.write_i16(i16)
end
def write_i32(i32)
@protocol.write_i32(i32)
end
def write_i64(i64)
@protocol.write_i64(i64)
end
def write_double(dub)
@protocol.write_double(dub)
end
def write_string(str)
@protocol.write_string(str)
end
def write_binary(buf)
@protocol.write_binary(buf)
end
def read_message_begin
@protocol.read_message_begin
end
def read_message_end
@protocol.read_message_end
end
def read_struct_begin
@protocol.read_struct_begin
end
def read_struct_end
@protocol.read_struct_end
end
def read_field_begin
@protocol.read_field_begin
end
def read_field_end
@protocol.read_field_end
end
def read_map_begin
@protocol.read_map_begin
end
def read_map_end
@protocol.read_map_end
end
def read_list_begin
@protocol.read_list_begin
end
def read_list_end
@protocol.read_list_end
end
def read_set_begin
@protocol.read_set_begin
end
def read_set_end
@protocol.read_set_end
end
def read_bool
@protocol.read_bool
end
def read_byte
@protocol.read_byte
end
def read_i16
@protocol.read_i16
end
def read_i32
@protocol.read_i32
end
def read_i64
@protocol.read_i64
end
def read_double
@protocol.read_double
end
def read_string
@protocol.read_string
end
def read_binary
@protocol.read_binary
end
end
end

View file

@ -0,0 +1,33 @@
#
# 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.
#
module Thrift
class Deserializer
def initialize(protocol_factory = BinaryProtocolFactory.new)
@transport = MemoryBufferTransport.new
@protocol = protocol_factory.get_protocol(@transport)
end
def deserialize(base, buffer)
@transport.reset_buffer(buffer)
base.read(@protocol)
base
end
end
end

View file

@ -0,0 +1,34 @@
#
# 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.
#
module Thrift
class Serializer
def initialize(protocol_factory = BinaryProtocolFactory.new)
@transport = MemoryBufferTransport.new
@protocol = protocol_factory.get_protocol(@transport)
end
def serialize(base)
@transport.reset_buffer
base.write(@protocol)
@transport.read(@transport.available)
end
end
end

View file

@ -0,0 +1,31 @@
#
# 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.
#
module Thrift
class BaseServer
def initialize(processor, server_transport, transport_factory=nil, protocol_factory=nil)
@processor = processor
@server_transport = server_transport
@transport_factory = transport_factory ? transport_factory : Thrift::BaseTransportFactory.new
@protocol_factory = protocol_factory ? protocol_factory : Thrift::BinaryProtocolFactory.new
end
def serve; nil; end
end
end

View file

@ -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.
#
require 'mongrel'
## Sticks a service on a URL, using mongrel to do the HTTP work
# <b>DEPRECATED:</b> Please use <tt>Thrift::ThinHTTPServer</tt> instead.
module Thrift
class MongrelHTTPServer < BaseServer
class Handler < Mongrel::HttpHandler
def initialize(processor, protocol_factory)
@processor = processor
@protocol_factory = protocol_factory
end
def process(request, response)
if request.params["REQUEST_METHOD"] == "POST"
response.start(200) do |head, out|
head["Content-Type"] = "application/x-thrift"
transport = IOStreamTransport.new request.body, out
protocol = @protocol_factory.get_protocol transport
@processor.process protocol, protocol
end
else
response.start(404) { }
end
end
end
def initialize(processor, opts={})
Kernel.warn "[DEPRECATION WARNING] `Thrift::MongrelHTTPServer` is deprecated. Please use `Thrift::ThinHTTPServer` instead."
port = opts[:port] || 80
ip = opts[:ip] || "0.0.0.0"
path = opts[:path] || ""
protocol_factory = opts[:protocol_factory] || BinaryProtocolFactory.new
@server = Mongrel::HttpServer.new ip, port
@server.register "/#{path}", Handler.new(processor, protocol_factory)
end
def serve
@server.run.join
end
end
end

View file

@ -0,0 +1,305 @@
#
# 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.
#
require 'logger'
require 'thread'
module Thrift
# this class expects to always use a FramedTransport for reading messages
class NonblockingServer < BaseServer
def initialize(processor, server_transport, transport_factory=nil, protocol_factory=nil, num=20, logger=nil)
super(processor, server_transport, transport_factory, protocol_factory)
@num_threads = num
if logger.nil?
@logger = Logger.new(STDERR)
@logger.level = Logger::WARN
else
@logger = logger
end
@shutdown_semaphore = Mutex.new
@transport_semaphore = Mutex.new
end
def serve
@logger.info "Starting #{self}"
@server_transport.listen
@io_manager = start_io_manager
begin
loop do
break if @server_transport.closed?
begin
rd, = select([@server_transport], nil, nil, 0.1)
rescue Errno::EBADF => e
# In Ruby 1.9, calling @server_transport.close in shutdown paths causes the select() to raise an
# Errno::EBADF. If this happens, ignore it and retry the loop.
break
end
next if rd.nil?
socket = @server_transport.accept
@logger.debug "Accepted socket: #{socket.inspect}"
@io_manager.add_connection socket
end
rescue IOError => e
end
# we must be shutting down
@logger.info "#{self} is shutting down, goodbye"
ensure
@transport_semaphore.synchronize do
@server_transport.close
end
@io_manager.ensure_closed unless @io_manager.nil?
end
def shutdown(timeout = 0, block = true)
@shutdown_semaphore.synchronize do
return if @is_shutdown
@is_shutdown = true
end
# nonblocking is intended for calling from within a Handler
# but we can't change the order of operations here, so lets thread
shutdown_proc = lambda do
@io_manager.shutdown(timeout)
@transport_semaphore.synchronize do
@server_transport.close # this will break the accept loop
end
end
if block
shutdown_proc.call
else
Thread.new &shutdown_proc
end
end
private
def start_io_manager
iom = IOManager.new(@processor, @server_transport, @transport_factory, @protocol_factory, @num_threads, @logger)
iom.spawn
iom
end
class IOManager # :nodoc:
DEFAULT_BUFFER = 2**20
def initialize(processor, server_transport, transport_factory, protocol_factory, num, logger)
@processor = processor
@server_transport = server_transport
@transport_factory = transport_factory
@protocol_factory = protocol_factory
@num_threads = num
@logger = logger
@connections = []
@buffers = Hash.new { |h,k| h[k] = '' }
@signal_queue = Queue.new
@signal_pipes = IO.pipe
@signal_pipes[1].sync = true
@worker_queue = Queue.new
@shutdown_queue = Queue.new
end
def add_connection(socket)
signal [:connection, socket]
end
def spawn
@iom_thread = Thread.new do
@logger.debug "Starting #{self}"
run
end
end
def shutdown(timeout = 0)
@logger.debug "#{self} is shutting down workers"
@worker_queue.clear
@num_threads.times { @worker_queue.push [:shutdown] }
signal [:shutdown, timeout]
@shutdown_queue.pop
@signal_pipes[0].close
@signal_pipes[1].close
@logger.debug "#{self} is shutting down, goodbye"
end
def ensure_closed
kill_worker_threads if @worker_threads
@iom_thread.kill
end
private
def run
spin_worker_threads
loop do
rd, = select([@signal_pipes[0], *@connections])
if rd.delete @signal_pipes[0]
break if read_signals == :shutdown
end
rd.each do |fd|
begin
if fd.handle.eof?
remove_connection fd
else
read_connection fd
end
rescue Errno::ECONNRESET
remove_connection fd
end
end
end
join_worker_threads(@shutdown_timeout)
ensure
@shutdown_queue.push :shutdown
end
def read_connection(fd)
@buffers[fd] << fd.read(DEFAULT_BUFFER)
while(frame = slice_frame!(@buffers[fd]))
@logger.debug "#{self} is processing a frame"
@worker_queue.push [:frame, fd, frame]
end
end
def spin_worker_threads
@logger.debug "#{self} is spinning up worker threads"
@worker_threads = []
@num_threads.times do
@worker_threads << spin_thread
end
end
def spin_thread
Worker.new(@processor, @transport_factory, @protocol_factory, @logger, @worker_queue).spawn
end
def signal(msg)
@signal_queue << msg
@signal_pipes[1].write " "
end
def read_signals
# clear the signal pipe
# note that since read_nonblock is broken in jruby,
# we can only read up to a set number of signals at once
sigstr = @signal_pipes[0].readpartial(1024)
# now read the signals
begin
sigstr.length.times do
signal, obj = @signal_queue.pop(true)
case signal
when :connection
@connections << obj
when :shutdown
@shutdown_timeout = obj
return :shutdown
end
end
rescue ThreadError
# out of signals
# note that in a perfect world this would never happen, since we're
# only reading the number of signals pushed on the pipe, but given the lack
# of locks, in theory we could clear the pipe/queue while a new signal is being
# placed on the pipe, at which point our next read_signals would hit this error
end
end
def remove_connection(fd)
# don't explicitly close it, a thread may still be writing to it
@connections.delete fd
@buffers.delete fd
end
def join_worker_threads(shutdown_timeout)
start = Time.now
@worker_threads.each do |t|
if shutdown_timeout > 0
timeout = (start + shutdown_timeout) - Time.now
break if timeout <= 0
t.join(timeout)
else
t.join
end
end
kill_worker_threads
end
def kill_worker_threads
@worker_threads.each do |t|
t.kill if t.status
end
@worker_threads.clear
end
def slice_frame!(buf)
if buf.length >= 4
size = buf.unpack('N').first
if buf.length >= size + 4
buf.slice!(0, size + 4)
else
nil
end
else
nil
end
end
class Worker # :nodoc:
def initialize(processor, transport_factory, protocol_factory, logger, queue)
@processor = processor
@transport_factory = transport_factory
@protocol_factory = protocol_factory
@logger = logger
@queue = queue
end
def spawn
Thread.new do
@logger.debug "#{self} is spawning"
run
end
end
private
def run
loop do
cmd, *args = @queue.pop
case cmd
when :shutdown
@logger.debug "#{self} is shutting down, goodbye"
break
when :frame
fd, frame = args
begin
otrans = @transport_factory.get_transport(fd)
oprot = @protocol_factory.get_protocol(otrans)
membuf = MemoryBufferTransport.new(frame)
itrans = @transport_factory.get_transport(membuf)
iprot = @protocol_factory.get_protocol(itrans)
@processor.process(iprot, oprot)
rescue => e
@logger.error "#{Thread.current.inspect} raised error: #{e.inspect}\n#{e.backtrace.join("\n")}"
end
end
end
end
end
end
end
end

View file

@ -0,0 +1,43 @@
#
# 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.
#
module Thrift
class SimpleServer < BaseServer
def serve
begin
@server_transport.listen
loop do
client = @server_transport.accept
trans = @transport_factory.get_transport(client)
prot = @protocol_factory.get_protocol(trans)
begin
loop do
@processor.process(prot, prot)
end
rescue Thrift::TransportException, Thrift::ProtocolException
ensure
trans.close
end
end
ensure
@server_transport.close
end
end
end
end

View file

@ -0,0 +1,91 @@
#
# 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.
#
require 'rack'
require 'thin'
##
# Wraps the Thin web server to provide a Thrift server over HTTP.
module Thrift
class ThinHTTPServer < BaseServer
##
# Accepts a Thrift::Processor
# Options include:
# * :port
# * :ip
# * :path
# * :protocol_factory
def initialize(processor, options={})
port = options[:port] || 80
ip = options[:ip] || "0.0.0.0"
path = options[:path] || "/"
protocol_factory = options[:protocol_factory] || BinaryProtocolFactory.new
app = RackApplication.for(path, processor, protocol_factory)
@server = Thin::Server.new(ip, port, app)
end
##
# Starts the server
def serve
@server.start
end
class RackApplication
THRIFT_HEADER = "application/x-thrift"
def self.for(path, processor, protocol_factory)
Rack::Builder.new do
use Rack::CommonLogger
use Rack::ShowExceptions
use Rack::Lint
map path do
run lambda { |env|
request = Rack::Request.new(env)
if RackApplication.valid_thrift_request?(request)
RackApplication.successful_request(request, processor, protocol_factory)
else
RackApplication.failed_request
end
}
end
end
end
def self.successful_request(rack_request, processor, protocol_factory)
response = Rack::Response.new([], 200, {'Content-Type' => THRIFT_HEADER})
transport = IOStreamTransport.new rack_request.body, response
protocol = protocol_factory.get_protocol transport
processor.process protocol, protocol
response
end
def self.failed_request
Rack::Response.new(['Not Found'], 404, {'Content-Type' => THRIFT_HEADER})
end
def self.valid_thrift_request?(rack_request)
rack_request.post? && rack_request.env["CONTENT_TYPE"] == THRIFT_HEADER
end
end
end
end

View file

@ -0,0 +1,75 @@
#
# 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.
#
require 'thread'
module Thrift
class ThreadPoolServer < BaseServer
def initialize(processor, server_transport, transport_factory=nil, protocol_factory=nil, num=20)
super(processor, server_transport, transport_factory, protocol_factory)
@thread_q = SizedQueue.new(num)
@exception_q = Queue.new
@running = false
end
## exceptions that happen in worker threads will be relayed here and
## must be caught. 'retry' can be used to continue. (threads will
## continue to run while the exception is being handled.)
def rescuable_serve
Thread.new { serve } unless @running
@running = true
raise @exception_q.pop
end
## exceptions that happen in worker threads simply cause that thread
## to die and another to be spawned in its place.
def serve
@server_transport.listen
begin
loop do
@thread_q.push(:token)
Thread.new do
begin
loop do
client = @server_transport.accept
trans = @transport_factory.get_transport(client)
prot = @protocol_factory.get_protocol(trans)
begin
loop do
@processor.process(prot, prot)
end
rescue Thrift::TransportException, Thrift::ProtocolException => e
ensure
trans.close
end
end
rescue => e
@exception_q.push(e)
ensure
@thread_q.pop # thread died!
end
end
end
ensure
@server_transport.close
end
end
end
end

View file

@ -0,0 +1,47 @@
#
# 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.
#
require 'thread'
module Thrift
class ThreadedServer < BaseServer
def serve
begin
@server_transport.listen
loop do
client = @server_transport.accept
trans = @transport_factory.get_transport(client)
prot = @protocol_factory.get_protocol(trans)
Thread.new(prot, trans) do |p, t|
begin
loop do
@processor.process(p, p)
end
rescue Thrift::TransportException, Thrift::ProtocolException
ensure
t.close
end
end
end
ensure
@server_transport.close
end
end
end
end

View file

@ -0,0 +1,237 @@
#
# 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.
#
require 'set'
module Thrift
module Struct
def initialize(d={}, &block)
# get a copy of the default values to work on, removing defaults in favor of arguments
fields_with_defaults = fields_with_default_values.dup
# check if the defaults is empty, or if there are no parameters for this
# instantiation, and if so, don't bother overriding defaults.
unless fields_with_defaults.empty? || d.empty?
d.each_key do |name|
fields_with_defaults.delete(name.to_s)
end
end
# assign all the user-specified arguments
unless d.empty?
d.each do |name, value|
unless name_to_id(name.to_s)
raise Exception, "Unknown key given to #{self.class}.new: #{name}"
end
Thrift.check_type(value, struct_fields[name_to_id(name.to_s)], name) if Thrift.type_checking
instance_variable_set("@#{name}", value)
end
end
# assign all the default values
unless fields_with_defaults.empty?
fields_with_defaults.each do |name, default_value|
instance_variable_set("@#{name}", (default_value.dup rescue default_value))
end
end
yield self if block_given?
end
def fields_with_default_values
fields_with_default_values = self.class.instance_variable_get(:@fields_with_default_values)
unless fields_with_default_values
fields_with_default_values = {}
struct_fields.each do |fid, field_def|
unless field_def[:default].nil?
fields_with_default_values[field_def[:name]] = field_def[:default]
end
end
self.class.instance_variable_set(:@fields_with_default_values, fields_with_default_values)
end
fields_with_default_values
end
def inspect(skip_optional_nulls = true)
fields = []
each_field do |fid, field_info|
name = field_info[:name]
value = instance_variable_get("@#{name}")
unless skip_optional_nulls && field_info[:optional] && value.nil?
fields << "#{name}:#{inspect_field(value, field_info)}"
end
end
"<#{self.class} #{fields.join(", ")}>"
end
def read(iprot)
iprot.read_struct_begin
loop do
fname, ftype, fid = iprot.read_field_begin
break if (ftype == Types::STOP)
handle_message(iprot, fid, ftype)
iprot.read_field_end
end
iprot.read_struct_end
validate
end
def write(oprot)
validate
oprot.write_struct_begin(self.class.name)
each_field do |fid, field_info|
name = field_info[:name]
type = field_info[:type]
value = instance_variable_get("@#{name}")
unless value.nil?
if is_container? type
oprot.write_field_begin(name, type, fid)
write_container(oprot, value, field_info)
oprot.write_field_end
else
oprot.write_field(field_info, fid, value)
end
end
end
oprot.write_field_stop
oprot.write_struct_end
end
def ==(other)
return false if other.nil?
each_field do |fid, field_info|
name = field_info[:name]
return false unless other.respond_to?(name) && self.send(name) == other.send(name)
end
true
end
def eql?(other)
self.class == other.class && self == other
end
# This implementation of hash() is inspired by Apache's Java HashCodeBuilder class.
def hash
total = 17
each_field do |fid, field_info|
name = field_info[:name]
value = self.send(name)
total = (total * 37 + value.hash) & 0xffffffff
end
total
end
def differences(other)
diffs = []
unless other.is_a?(self.class)
diffs << "Different class!"
else
each_field do |fid, field_info|
name = field_info[:name]
diffs << "#{name} differs!" unless self.instance_variable_get("@#{name}") == other.instance_variable_get("@#{name}")
end
end
diffs
end
def self.field_accessor(klass, field_info)
field_name_sym = field_info[:name].to_sym
klass.send :attr_reader, field_name_sym
klass.send :define_method, "#{field_info[:name]}=" do |value|
Thrift.check_type(value, field_info, field_info[:name]) if Thrift.type_checking
instance_variable_set("@#{field_name_sym}", value)
end
end
def self.generate_accessors(klass)
klass::FIELDS.values.each do |field_info|
field_accessor(klass, field_info)
qmark_isset_method(klass, field_info)
end
end
def self.qmark_isset_method(klass, field_info)
klass.send :define_method, "#{field_info[:name]}?" do
!self.send(field_info[:name].to_sym).nil?
end
end
def <=>(other)
if self.class == other.class
each_field do |fid, field_info|
v1 = self.send(field_info[:name])
v1_set = !v1.nil?
v2 = other.send(field_info[:name])
v2_set = !v2.nil?
if v1_set && !v2_set
return -1
elsif !v1_set && v2_set
return 1
elsif v1_set && v2_set
cmp = v1 <=> v2
if cmp != 0
return cmp
end
end
end
0
else
self.class <=> other.class
end
end
protected
def self.append_features(mod)
if mod.ancestors.include? ::Exception
mod.send :class_variable_set, :'@@__thrift_struct_real_initialize', mod.instance_method(:initialize)
super
# set up our custom initializer so `raise Xception, 'message'` works
mod.send :define_method, :struct_initialize, mod.instance_method(:initialize)
mod.send :define_method, :initialize, mod.instance_method(:exception_initialize)
else
super
end
end
def exception_initialize(*args, &block)
if args.size == 1 and args.first.is_a? Hash
# looks like it's a regular Struct initialize
method(:struct_initialize).call(args.first)
else
# call the Struct initializer first with no args
# this will set our field default values
method(:struct_initialize).call()
# now give it to the exception
self.class.send(:class_variable_get, :'@@__thrift_struct_real_initialize').bind(self).call(*args, &block) if args.size > 0
# self.class.instance_method(:initialize).bind(self).call(*args, &block)
end
end
def handle_message(iprot, fid, ftype)
field = struct_fields[fid]
if field and field[:type] == ftype
value = read_field(iprot, field)
instance_variable_set("@#{field[:name]}", value)
else
iprot.skip(ftype)
end
end
end
end

View file

@ -0,0 +1,192 @@
#
# 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.
#
require 'set'
module Thrift
module Struct_Union
def name_to_id(name)
names_to_ids = self.class.instance_variable_get(:@names_to_ids)
unless names_to_ids
names_to_ids = {}
struct_fields.each do |fid, field_def|
names_to_ids[field_def[:name]] = fid
end
self.class.instance_variable_set(:@names_to_ids, names_to_ids)
end
names_to_ids[name]
end
def sorted_field_ids
sorted_field_ids = self.class.instance_variable_get(:@sorted_field_ids)
unless sorted_field_ids
sorted_field_ids = struct_fields.keys.sort
self.class.instance_variable_set(:@sorted_field_ids, sorted_field_ids)
end
sorted_field_ids
end
def each_field
sorted_field_ids.each do |fid|
data = struct_fields[fid]
yield fid, data
end
end
def read_field(iprot, field = {})
case field[:type]
when Types::STRUCT
value = field[:class].new
value.read(iprot)
when Types::MAP
key_type, val_type, size = iprot.read_map_begin
# Skip the map contents if the declared key or value types don't match the expected ones.
if (size != 0 && (key_type != field[:key][:type] || val_type != field[:value][:type]))
size.times do
iprot.skip(key_type)
iprot.skip(val_type)
end
value = nil
else
value = {}
size.times do
k = read_field(iprot, field_info(field[:key]))
v = read_field(iprot, field_info(field[:value]))
value[k] = v
end
end
iprot.read_map_end
when Types::LIST
e_type, size = iprot.read_list_begin
# Skip the list contents if the declared element type doesn't match the expected one.
if (e_type != field[:element][:type])
size.times do
iprot.skip(e_type)
end
value = nil
else
value = Array.new(size) do |n|
read_field(iprot, field_info(field[:element]))
end
end
iprot.read_list_end
when Types::SET
e_type, size = iprot.read_set_begin
# Skip the set contents if the declared element type doesn't match the expected one.
if (e_type != field[:element][:type])
size.times do
iprot.skip(e_type)
end
else
value = Set.new
size.times do
element = read_field(iprot, field_info(field[:element]))
value << element
end
end
iprot.read_set_end
else
value = iprot.read_type(field)
end
value
end
def write_data(oprot, value, field)
if is_container? field[:type]
write_container(oprot, value, field)
else
oprot.write_type(field, value)
end
end
def write_container(oprot, value, field = {})
case field[:type]
when Types::MAP
oprot.write_map_begin(field[:key][:type], field[:value][:type], value.size)
value.each do |k, v|
write_data(oprot, k, field[:key])
write_data(oprot, v, field[:value])
end
oprot.write_map_end
when Types::LIST
oprot.write_list_begin(field[:element][:type], value.size)
value.each do |elem|
write_data(oprot, elem, field[:element])
end
oprot.write_list_end
when Types::SET
oprot.write_set_begin(field[:element][:type], value.size)
value.each do |v,| # the , is to preserve compatibility with the old Hash-style sets
write_data(oprot, v, field[:element])
end
oprot.write_set_end
else
raise "Not a container type: #{field[:type]}"
end
end
CONTAINER_TYPES = []
CONTAINER_TYPES[Types::LIST] = true
CONTAINER_TYPES[Types::MAP] = true
CONTAINER_TYPES[Types::SET] = true
def is_container?(type)
CONTAINER_TYPES[type]
end
def field_info(field)
{ :type => field[:type],
:class => field[:class],
:key => field[:key],
:value => field[:value],
:element => field[:element] }
end
def inspect_field(value, field_info)
if enum_class = field_info[:enum_class]
"#{enum_class.const_get(:VALUE_MAP)[value]} (#{value})"
elsif value.is_a? Hash
if field_info[:type] == Types::MAP
map_buf = []
value.each do |k, v|
map_buf << inspect_field(k, field_info[:key]) + ": " + inspect_field(v, field_info[:value])
end
"{" + map_buf.join(", ") + "}"
else
# old-style set
inspect_collection(value.keys, field_info)
end
elsif value.is_a? Array
inspect_collection(value, field_info)
elsif value.is_a? Set
inspect_collection(value, field_info)
elsif value.is_a?(String) && field_info[:binary]
value.unpack("H*").first
else
value.inspect
end
end
def inspect_collection(collection, field_info)
buf = []
collection.each do |k|
buf << inspect_field(k, field_info[:element])
end
"[" + buf.join(", ") + "]"
end
end
end

View file

@ -0,0 +1,24 @@
#
# 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.
#
begin
require "thrift_native"
rescue LoadError
puts "Unable to load thrift_native extension. Defaulting to pure Ruby libraries."
end

View file

@ -0,0 +1,37 @@
# encoding: ascii-8bit
#
# 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.
#
module Thrift
class BaseServerTransport
def listen
raise NotImplementedError
end
def accept
raise NotImplementedError
end
def close; nil; end
def closed?
raise NotImplementedError
end
end
end

View file

@ -0,0 +1,109 @@
# encoding: ascii-8bit
#
# 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.
#
module Thrift
class TransportException < Exception
UNKNOWN = 0
NOT_OPEN = 1
ALREADY_OPEN = 2
TIMED_OUT = 3
END_OF_FILE = 4
attr_reader :type
def initialize(type=UNKNOWN, message=nil)
super(message)
@type = type
end
end
module TransportUtils
# Deprecated: Use Thrift::Bytes instead
def self.get_string_byte(string, index)
Bytes.get_string_byte(string, index)
end
# Deprecated: Use Thrift::Bytes instead
def self.set_string_byte(string, index, byte)
Bytes.set_string_byte(string, index, byte)
end
end
class BaseTransport
def open?; end
def open; end
def close; end
# Reads a number of bytes from the transports. In Ruby 1.9+, the String returned will have a BINARY (aka ASCII8BIT) encoding.
#
# sz - The number of bytes to read from the transport.
#
# Returns a String acting as a byte buffer.
def read(sz)
raise NotImplementedError
end
# Returns an unsigned byte as a Fixnum in the range (0..255).
def read_byte
buf = read_all(1)
return Bytes.get_string_byte(buf, 0)
end
# Reads size bytes and copies them into buffer[0..size].
def read_into_buffer(buffer, size)
tmp = read_all(size)
i = 0
tmp.each_byte do |byte|
Bytes.set_string_byte(buffer, i, byte)
i += 1
end
i
end
def read_all(size)
return Bytes.empty_byte_buffer if size <= 0
buf = read(size)
while (buf.length < size)
chunk = read(size - buf.length)
buf << chunk
end
buf
end
# Writes the byte buffer to the transport. In Ruby 1.9+, the buffer will be forced into BINARY encoding.
#
# buf - A String acting as a byte buffer.
#
# Returns nothing.
def write(buf); end
alias_method :<<, :write
def flush; end
end
class BaseTransportFactory
def get_transport(trans)
return trans
end
end
end

View file

@ -0,0 +1,114 @@
# encoding: ascii-8bit
#
# 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.
#
module Thrift
class BufferedTransport < BaseTransport
DEFAULT_BUFFER = 4096
def initialize(transport)
@transport = transport
@wbuf = Bytes.empty_byte_buffer
@rbuf = Bytes.empty_byte_buffer
@index = 0
end
def open?
return @transport.open?
end
def open
@transport.open
end
def close
flush
@transport.close
end
def read(sz)
@index += sz
ret = @rbuf.slice(@index - sz, sz) || Bytes.empty_byte_buffer
if ret.length == 0
@rbuf = @transport.read([sz, DEFAULT_BUFFER].max)
@index = sz
ret = @rbuf.slice(0, sz) || Bytes.empty_byte_buffer
end
ret
end
def read_byte
# If the read buffer is exhausted, try to read up to DEFAULT_BUFFER more bytes into it.
if @index >= @rbuf.size
@rbuf = @transport.read(DEFAULT_BUFFER)
@index = 0
end
# The read buffer has some data now, read a single byte. Using get_string_byte() avoids
# allocating a temp string of size 1 unnecessarily.
@index += 1
return Bytes.get_string_byte(@rbuf, @index - 1)
end
# Reads a number of bytes from the transport into the buffer passed.
#
# buffer - The String (byte buffer) to write data to; this is assumed to have a BINARY encoding.
# size - The number of bytes to read from the transport and write to the buffer.
#
# Returns the number of bytes read.
def read_into_buffer(buffer, size)
i = 0
while i < size
# If the read buffer is exhausted, try to read up to DEFAULT_BUFFER more bytes into it.
if @index >= @rbuf.size
@rbuf = @transport.read(DEFAULT_BUFFER)
@index = 0
end
# The read buffer has some data now, so copy bytes over to the output buffer.
byte = Bytes.get_string_byte(@rbuf, @index)
Bytes.set_string_byte(buffer, i, byte)
@index += 1
i += 1
end
i
end
def write(buf)
@wbuf << Bytes.force_binary_encoding(buf)
end
def flush
unless @wbuf.empty?
@transport.write(@wbuf)
@wbuf = Bytes.empty_byte_buffer
end
@transport.flush
end
end
class BufferedTransportFactory < BaseTransportFactory
def get_transport(transport)
return BufferedTransport.new(transport)
end
end
end

View file

@ -0,0 +1,117 @@
# encoding: ascii-8bit
#
# 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.
#
module Thrift
class FramedTransport < BaseTransport
def initialize(transport, read=true, write=true)
@transport = transport
@rbuf = Bytes.empty_byte_buffer
@wbuf = Bytes.empty_byte_buffer
@read = read
@write = write
@index = 0
end
def open?
@transport.open?
end
def open
@transport.open
end
def close
@transport.close
end
def read(sz)
return @transport.read(sz) unless @read
return Bytes.empty_byte_buffer if sz <= 0
read_frame if @index >= @rbuf.length
@index += sz
@rbuf.slice(@index - sz, sz) || Bytes.empty_byte_buffer
end
def read_byte
return @transport.read_byte() unless @read
read_frame if @index >= @rbuf.length
# The read buffer has some data now, read a single byte. Using get_string_byte() avoids
# allocating a temp string of size 1 unnecessarily.
@index += 1
return Bytes.get_string_byte(@rbuf, @index - 1)
end
def read_into_buffer(buffer, size)
i = 0
while i < size
read_frame if @index >= @rbuf.length
# The read buffer has some data now, so copy bytes over to the output buffer.
byte = Bytes.get_string_byte(@rbuf, @index)
Bytes.set_string_byte(buffer, i, byte)
@index += 1
i += 1
end
i
end
def write(buf, sz=nil)
return @transport.write(buf) unless @write
buf = Bytes.force_binary_encoding(buf)
@wbuf << (sz ? buf[0...sz] : buf)
end
#
# Writes the output buffer to the stream in the format of a 4-byte length
# followed by the actual data.
#
def flush
return @transport.flush unless @write
out = [@wbuf.length].pack('N')
# Array#pack should return a BINARY encoded String, so it shouldn't be necessary to force encoding
out << @wbuf
@transport.write(out)
@transport.flush
@wbuf = Bytes.empty_byte_buffer
end
private
def read_frame
sz = @transport.read_all(4).unpack('N').first
@index = 0
@rbuf = @transport.read_all(sz)
end
end
class FramedTransportFactory < BaseTransportFactory
def get_transport(transport)
return FramedTransport.new(transport)
end
end
end

View file

@ -0,0 +1,57 @@
# encoding: ascii-8bit
#
# 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.
#
require 'net/http'
require 'net/https'
require 'openssl'
require 'uri'
require 'stringio'
module Thrift
class HTTPClientTransport < BaseTransport
def initialize(url, opts = {})
@url = URI url
@headers = {'Content-Type' => 'application/x-thrift'}
@outbuf = Bytes.empty_byte_buffer
@ssl_verify_mode = opts.fetch(:ssl_verify_mode, OpenSSL::SSL::VERIFY_PEER)
end
def open?; true end
def read(sz); @inbuf.read sz end
def write(buf); @outbuf << Bytes.force_binary_encoding(buf) end
def add_headers(headers)
@headers = @headers.merge(headers)
end
def flush
http = Net::HTTP.new @url.host, @url.port
http.use_ssl = @url.scheme == 'https'
http.verify_mode = @ssl_verify_mode if @url.scheme == 'https'
resp = http.post(@url.request_uri, @outbuf, @headers)
data = resp.body
data = Bytes.force_binary_encoding(data)
@inbuf = StringIO.new data
ensure
@outbuf = Bytes.empty_byte_buffer
end
end
end

View file

@ -0,0 +1,39 @@
# encoding: ascii-8bit
#
# 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.
#
# Very very simple implementation of wrapping two objects, one with a #read
# method and one with a #write method, into a transport for thrift.
#
# Assumes both objects are open, remain open, don't require flushing, etc.
#
module Thrift
class IOStreamTransport < BaseTransport
def initialize(input, output)
@input = input
@output = output
end
def open?; not @input.closed? or not @output.closed? end
def read(sz); @input.read(sz) end
def write(buf); @output.write(Bytes.force_binary_encoding(buf)) end
def close; @input.close; @output.close end
def to_io; @input end # we're assuming this is used in a IO.select for reading
end
end

View file

@ -0,0 +1,125 @@
# encoding: ascii-8bit
#
# 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.
#
module Thrift
class MemoryBufferTransport < BaseTransport
GARBAGE_BUFFER_SIZE = 4*(2**10) # 4kB
# If you pass a string to this, you should #dup that string
# unless you want it to be modified by #read and #write
#--
# this behavior is no longer required. If you wish to change it
# go ahead, just make sure the specs pass
def initialize(buffer = nil)
@buf = buffer ? Bytes.force_binary_encoding(buffer) : Bytes.empty_byte_buffer
@index = 0
end
def open?
return true
end
def open
end
def close
end
def peek
@index < @buf.size
end
# this method does not use the passed object directly but copies it
def reset_buffer(new_buf = '')
@buf.replace Bytes.force_binary_encoding(new_buf)
@index = 0
end
def available
@buf.length - @index
end
def read(len)
data = @buf.slice(@index, len)
@index += len
@index = @buf.size if @index > @buf.size
if @index >= GARBAGE_BUFFER_SIZE
@buf = @buf.slice(@index..-1)
@index = 0
end
if data.size < len
raise EOFError, "Not enough bytes remain in buffer"
end
data
end
def read_byte
raise EOFError.new("Not enough bytes remain in buffer") if @index >= @buf.size
val = Bytes.get_string_byte(@buf, @index)
@index += 1
if @index >= GARBAGE_BUFFER_SIZE
@buf = @buf.slice(@index..-1)
@index = 0
end
val
end
def read_into_buffer(buffer, size)
i = 0
while i < size
raise EOFError.new("Not enough bytes remain in buffer") if @index >= @buf.size
# The read buffer has some data now, so copy bytes over to the output buffer.
byte = Bytes.get_string_byte(@buf, @index)
Bytes.set_string_byte(buffer, i, byte)
@index += 1
i += 1
end
if @index >= GARBAGE_BUFFER_SIZE
@buf = @buf.slice(@index..-1)
@index = 0
end
i
end
def write(wbuf)
@buf << Bytes.force_binary_encoding(wbuf)
end
def flush
end
def inspect_buffer
out = []
for idx in 0...(@buf.size)
# if idx != 0
# out << " "
# end
if idx == @index
out << ">"
end
out << @buf[idx].ord.to_s(16)
end
out.join(" ")
end
end
end

View file

@ -0,0 +1,63 @@
# encoding: ascii-8bit
#
# 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.
#
require 'socket'
module Thrift
class ServerSocket < BaseServerTransport
# call-seq: initialize(host = nil, port)
def initialize(host_or_port, port = nil)
if port
@host = host_or_port
@port = port
else
@host = nil
@port = host_or_port
end
@handle = nil
end
attr_reader :handle
def listen
@handle = TCPServer.new(@host, @port)
end
def accept
unless @handle.nil?
sock = @handle.accept
trans = Socket.new
trans.handle = sock
trans
end
end
def close
@handle.close unless @handle.nil? or @handle.closed?
@handle = nil
end
def closed?
@handle.nil? or @handle.closed?
end
alias to_io handle
end
end

View file

@ -0,0 +1,141 @@
# encoding: ascii-8bit
#
# 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.
#
require 'socket'
module Thrift
class Socket < BaseTransport
def initialize(host='localhost', port=9090, timeout=nil)
@host = host
@port = port
@timeout = timeout
@desc = "#{host}:#{port}"
@handle = nil
end
attr_accessor :handle, :timeout
def open
for addrinfo in ::Socket::getaddrinfo(@host, @port, nil, ::Socket::SOCK_STREAM) do
begin
socket = ::Socket.new(addrinfo[4], ::Socket::SOCK_STREAM, 0)
socket.setsockopt(::Socket::IPPROTO_TCP, ::Socket::TCP_NODELAY, 1)
sockaddr = ::Socket.sockaddr_in(addrinfo[1], addrinfo[3])
begin
socket.connect_nonblock(sockaddr)
rescue Errno::EINPROGRESS
unless IO.select(nil, [ socket ], nil, @timeout)
next
end
begin
socket.connect_nonblock(sockaddr)
rescue Errno::EISCONN
end
end
return @handle = socket
rescue StandardError => e
next
end
end
raise TransportException.new(TransportException::NOT_OPEN, "Could not connect to #{@desc}: #{e}")
end
def open?
!@handle.nil? and !@handle.closed?
end
def write(str)
raise IOError, "closed stream" unless open?
str = Bytes.force_binary_encoding(str)
begin
if @timeout.nil? or @timeout == 0
@handle.write(str)
else
len = 0
start = Time.now
while Time.now - start < @timeout
rd, wr, = IO.select(nil, [@handle], nil, @timeout)
if wr and not wr.empty?
len += @handle.write_nonblock(str[len..-1])
break if len >= str.length
end
end
if len < str.length
raise TransportException.new(TransportException::TIMED_OUT, "Socket: Timed out writing #{str.length} bytes to #{@desc}")
else
len
end
end
rescue TransportException => e
# pass this on
raise e
rescue StandardError => e
@handle.close
@handle = nil
raise TransportException.new(TransportException::NOT_OPEN, e.message)
end
end
def read(sz)
raise IOError, "closed stream" unless open?
begin
if @timeout.nil? or @timeout == 0
data = @handle.readpartial(sz)
else
# it's possible to interrupt select for something other than the timeout
# so we need to ensure we've waited long enough, but not too long
start = Time.now
timespent = 0
rd = loop do
rd, = IO.select([@handle], nil, nil, @timeout - timespent)
timespent = Time.now - start
break rd if (rd and not rd.empty?) or timespent >= @timeout
end
if rd.nil? or rd.empty?
raise TransportException.new(TransportException::TIMED_OUT, "Socket: Timed out reading #{sz} bytes from #{@desc}")
else
data = @handle.readpartial(sz)
end
end
rescue TransportException => e
# don't let this get caught by the StandardError handler
raise e
rescue StandardError => e
@handle.close unless @handle.closed?
@handle = nil
raise TransportException.new(TransportException::NOT_OPEN, e.message)
end
if (data.nil? or data.length == 0)
raise TransportException.new(TransportException::UNKNOWN, "Socket: Could not read #{sz} bytes from #{@desc}")
end
data
end
def close
@handle.close unless @handle.nil? or @handle.closed?
@handle = nil
end
def to_io
@handle
end
end
end

View file

@ -0,0 +1,37 @@
# encoding: ascii-8bit
#
# 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.
#
require 'socket'
module Thrift
class SSLServerSocket < ServerSocket
def initialize(host_or_port, port = nil, ssl_context = nil)
super(host_or_port, port)
@ssl_context = ssl_context
end
attr_accessor :ssl_context
def listen
socket = TCPServer.new(@host, @port)
@handle = OpenSSL::SSL::SSLServer.new(socket, @ssl_context)
end
end
end

View file

@ -0,0 +1,47 @@
# encoding: ascii-8bit
#
# 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.
module Thrift
class SSLSocket < Socket
def initialize(host='localhost', port=9090, timeout=nil, ssl_context=nil)
super(host, port, timeout)
@ssl_context = ssl_context
end
attr_accessor :ssl_context
def open
socket = super
@handle = OpenSSL::SSL::SSLSocket.new(socket, @ssl_context)
begin
@handle.connect_nonblock
@handle.post_connection_check(@host)
@handle
rescue IO::WaitReadable
IO.select([ @handle ], nil, nil, @timeout)
retry
rescue IO::WaitWritable
IO.select(nil, [ @handle ], nil, @timeout)
retry
rescue StandardError => e
raise TransportException.new(TransportException::NOT_OPEN, "Could not connect to #{@desc}: #{e}")
end
end
end
end

View file

@ -0,0 +1,60 @@
# encoding: ascii-8bit
#
# 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.
#
require 'socket'
module Thrift
class UNIXServerSocket < BaseServerTransport
def initialize(path)
@path = path
@handle = nil
end
attr_accessor :handle
def listen
@handle = ::UNIXServer.new(@path)
end
def accept
unless @handle.nil?
sock = @handle.accept
trans = UNIXSocket.new(nil)
trans.handle = sock
trans
end
end
def close
if @handle
@handle.close unless @handle.closed?
@handle = nil
# UNIXServer doesn't delete the socket file, so we have to do it ourselves
File.delete(@path)
end
end
def closed?
@handle.nil? or @handle.closed?
end
alias to_io handle
end
end

View file

@ -0,0 +1,40 @@
# encoding: ascii-8bit
#
# 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.
#
require 'socket'
module Thrift
class UNIXSocket < Socket
def initialize(path, timeout=nil)
@path = path
@timeout = timeout
@desc = @path # for read()'s error
@handle = nil
end
def open
begin
@handle = ::UNIXSocket.new(@path)
rescue StandardError
raise TransportException.new(TransportException::NOT_OPEN, "Could not open UNIX socket at #{@path}")
end
end
end
end

View file

@ -0,0 +1,101 @@
#
# 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.
#
require 'set'
module Thrift
module Types
STOP = 0
VOID = 1
BOOL = 2
BYTE = 3
DOUBLE = 4
I16 = 6
I32 = 8
I64 = 10
STRING = 11
STRUCT = 12
MAP = 13
SET = 14
LIST = 15
end
class << self
attr_accessor :type_checking
end
class TypeError < Exception
end
def self.check_type(value, field, name, skip_nil=true)
return if value.nil? and skip_nil
klasses = case field[:type]
when Types::VOID
NilClass
when Types::BOOL
[TrueClass, FalseClass]
when Types::BYTE, Types::I16, Types::I32, Types::I64
Integer
when Types::DOUBLE
Float
when Types::STRING
String
when Types::STRUCT
[Struct, Union]
when Types::MAP
Hash
when Types::SET
Set
when Types::LIST
Array
end
valid = klasses && [*klasses].any? { |klass| klass === value }
raise TypeError, "Expected #{type_name(field[:type])}, received #{value.class} for field #{name}" unless valid
# check elements now
case field[:type]
when Types::MAP
value.each_pair do |k,v|
check_type(k, field[:key], "#{name}.key", false)
check_type(v, field[:value], "#{name}.value", false)
end
when Types::SET, Types::LIST
value.each do |el|
check_type(el, field[:element], "#{name}.element", false)
end
when Types::STRUCT
raise TypeError, "Expected #{field[:class]}, received #{value.class} for field #{name}" unless field[:class] == value.class
end
end
def self.type_name(type)
Types.constants.each do |const|
return "Types::#{const}" if Types.const_get(const) == type
end
nil
end
module MessageTypes
CALL = 1
REPLY = 2
EXCEPTION = 3
ONEWAY = 4
end
end
Thrift.type_checking = false if Thrift.type_checking.nil?

View file

@ -0,0 +1,176 @@
#
# 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.
#
module Thrift
class Union
def initialize(name=nil, value=nil)
if name
if name.is_a? Hash
if name.size > 1
raise "#{self.class} cannot be instantiated with more than one field!"
end
name, value = name.keys.first, name.values.first
end
if Thrift.type_checking
raise Exception, "#{self.class} does not contain a field named #{name}!" unless name_to_id(name.to_s)
end
if value.nil?
raise Exception, "Union #{self.class} cannot be instantiated with setfield and nil value!"
end
Thrift.check_type(value, struct_fields[name_to_id(name.to_s)], name) if Thrift.type_checking
elsif !value.nil?
raise Exception, "Value provided, but no name!"
end
@setfield = name
@value = value
end
def inspect
if get_set_field
"<#{self.class} #{@setfield}: #{inspect_field(@value, struct_fields[name_to_id(@setfield.to_s)])}>"
else
"<#{self.class} >"
end
end
def read(iprot)
iprot.read_struct_begin
fname, ftype, fid = iprot.read_field_begin
handle_message(iprot, fid, ftype)
iprot.read_field_end
fname, ftype, fid = iprot.read_field_begin
raise "Too many fields for union" unless (ftype == Types::STOP)
iprot.read_struct_end
validate
end
def write(oprot)
validate
oprot.write_struct_begin(self.class.name)
fid = self.name_to_id(@setfield.to_s)
field_info = struct_fields[fid]
type = field_info[:type]
if is_container? type
oprot.write_field_begin(@setfield, type, fid)
write_container(oprot, @value, field_info)
oprot.write_field_end
else
oprot.write_field(@setfield, type, fid, @value)
end
oprot.write_field_stop
oprot.write_struct_end
end
def ==(other)
other.equal?(self) || other.instance_of?(self.class) && @setfield == other.get_set_field && @value == other.get_value
end
alias_method :eql?, :==
def hash
[self.class.name, @setfield, @value].hash
end
def self.field_accessor(klass, field_info)
klass.send :define_method, field_info[:name] do
if field_info[:name].to_sym == @setfield
@value
else
raise RuntimeError, "#{field_info[:name]} is not union's set field."
end
end
klass.send :define_method, "#{field_info[:name]}=" do |value|
Thrift.check_type(value, field_info, field_info[:name]) if Thrift.type_checking
@setfield = field_info[:name].to_sym
@value = value
end
end
def self.qmark_isset_method(klass, field_info)
klass.send :define_method, "#{field_info[:name]}?" do
get_set_field == field_info[:name].to_sym && !get_value.nil?
end
end
def self.generate_accessors(klass)
klass::FIELDS.values.each do |field_info|
field_accessor(klass, field_info)
qmark_isset_method(klass, field_info)
end
end
# get the symbol that indicates what the currently set field type is.
def get_set_field
@setfield
end
# get the current value of this union, regardless of what the set field is.
# generally, you should only use this method when you don't know in advance
# what field to expect.
def get_value
@value
end
def <=>(other)
if self.class == other.class
if get_set_field == other.get_set_field
if get_set_field.nil?
0
else
get_value <=> other.get_value
end
else
if get_set_field && other.get_set_field.nil?
-1
elsif get_set_field.nil? && other.get_set_field
1
elsif get_set_field.nil? && other.get_set_field.nil?
0
else
name_to_id(get_set_field.to_s) <=> name_to_id(other.get_set_field.to_s)
end
end
else
self.class <=> other.class
end
end
protected
def handle_message(iprot, fid, ftype)
field = struct_fields[fid]
if field and field[:type] == ftype
@value = read_field(iprot, field)
name = field[:name].to_sym
@setfield = name
else
iprot.skip(ftype)
end
end
end
end

View file

@ -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.
#
namespace rb Base
struct Hello {
1: string greeting = "hello world"
}
service BaseService {
Hello greeting(1:bool english)
}

View file

@ -0,0 +1,25 @@
# 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.
#
namespace rb Extended
include "BaseService.thrift"
service ExtendedService extends BaseService.BaseService {
void ping()
}

View file

@ -0,0 +1,44 @@
/*
* 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.
*/
#
# 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.
#
namespace rb OtherNamespace
enum SomeEnum {
ONE
TWO
}

View file

@ -0,0 +1,53 @@
/*
* 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.
*/
#
# 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.
#
namespace rb NamespacedSpecNamespace
include "Referenced.thrift"
struct Hello {
1: string greeting = "hello world"
}
service NamespacedNonblockingService {
Hello greeting(1:bool english)
bool block()
oneway void unblock(1:i32 n)
oneway void shutdown()
void sleep(1:double seconds)
}

View file

@ -0,0 +1,183 @@
/*
* 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.
*/
#
# 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.
#
namespace rb SpecNamespace
struct Hello {
1: string greeting = "hello world"
}
enum SomeEnum {
ONE
TWO
}
struct StructWithSomeEnum {
1: SomeEnum some_enum;
}
union TestUnion {
/**
* A doc string
*/
1: string string_field;
2: i32 i32_field;
3: i32 other_i32_field;
4: SomeEnum enum_field;
5: binary binary_field;
}
struct Foo {
1: i32 simple = 53,
2: string words = "words",
3: Hello hello = {'greeting' : "hello, world!"},
4: list<i32> ints = [1, 2, 2, 3],
5: map<i32, map<string, double>> complex,
6: set<i16> shorts = [5, 17, 239],
7: optional string opt_string
8: bool my_bool
}
struct Foo2 {
1: binary my_binary
}
struct BoolStruct {
1: bool yesno = 1
}
struct SimpleList {
1: list<bool> bools,
2: list<byte> bytes,
3: list<i16> i16s,
4: list<i32> i32s,
5: list<i64> i64s,
6: list<double> doubles,
7: list<string> strings,
8: list<map<i16, i16>> maps,
9: list<list<i16>> lists,
10: list<set<i16>> sets,
11: list<Hello> hellos
}
exception Xception {
1: string message,
2: i32 code = 1
}
service NonblockingService {
Hello greeting(1:bool english)
bool block()
oneway void unblock(1:i32 n)
oneway void shutdown()
void sleep(1:double seconds)
}
union My_union {
1: bool im_true,
2: byte a_bite,
3: i16 integer16,
4: i32 integer32,
5: i64 integer64,
6: double double_precision,
7: string some_characters,
8: i32 other_i32
9: SomeEnum some_enum;
10: map<SomeEnum, list<SomeEnum>> my_map;
}
struct Struct_with_union {
1: My_union fun_union
2: i32 integer32
3: string some_characters
}
struct StructWithEnumMap {
1: map<SomeEnum, list<SomeEnum>> my_map;
}
# Nested lists
struct NestedListInList {
1: list<list<byte>> value
}
struct NestedListInSet {
1: set<list<byte>> value
}
struct NestedListInMapKey {
1: map<list<byte>, byte> value
}
struct NestedListInMapValue {
1: map<byte, list<byte>> value
}
# Nested sets
struct NestedSetInList {
1: list<set<byte>> value
}
struct NestedSetInSet {
1: set<set<byte>> value
}
struct NestedSetInMapKey {
1: map<set<byte>, byte> value
}
struct NestedSetInMapValue {
1: map<byte, set<byte>> value
}
# Nested maps
struct NestedMapInList {
1: list<map<byte, byte>> value
}
struct NestedMapInSet {
1: set<map<byte, byte>> value
}
struct NestedMapInMapKey {
2: map<map<byte, byte>, byte> value
}
struct NestedMapInMapValue {
2: map<byte, map<byte, byte>> value
}

View file

@ -0,0 +1,217 @@
#
# 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.
#
require 'spec_helper'
describe 'BaseProtocol' do
before(:each) do
@trans = mock("MockTransport")
@prot = Thrift::BaseProtocol.new(@trans)
end
describe Thrift::BaseProtocol do
# most of the methods are stubs, so we can ignore them
it "should make trans accessible" do
@prot.trans.should eql(@trans)
end
it 'should write out a field nicely (deprecated write_field signature)' do
@prot.should_receive(:write_field_begin).with('field', 'type', 'fid').ordered
@prot.should_receive(:write_type).with({:name => 'field', :type => 'type'}, 'value').ordered
@prot.should_receive(:write_field_end).ordered
@prot.write_field('field', 'type', 'fid', 'value')
end
it 'should write out a field nicely' do
@prot.should_receive(:write_field_begin).with('field', 'type', 'fid').ordered
@prot.should_receive(:write_type).with({:name => 'field', :type => 'type', :binary => false}, 'value').ordered
@prot.should_receive(:write_field_end).ordered
@prot.write_field({:name => 'field', :type => 'type', :binary => false}, 'fid', 'value')
end
it 'should write out the different types (deprecated write_type signature)' do
@prot.should_receive(:write_bool).with('bool').ordered
@prot.should_receive(:write_byte).with('byte').ordered
@prot.should_receive(:write_double).with('double').ordered
@prot.should_receive(:write_i16).with('i16').ordered
@prot.should_receive(:write_i32).with('i32').ordered
@prot.should_receive(:write_i64).with('i64').ordered
@prot.should_receive(:write_string).with('string').ordered
struct = mock('Struct')
struct.should_receive(:write).with(@prot).ordered
@prot.write_type(Thrift::Types::BOOL, 'bool')
@prot.write_type(Thrift::Types::BYTE, 'byte')
@prot.write_type(Thrift::Types::DOUBLE, 'double')
@prot.write_type(Thrift::Types::I16, 'i16')
@prot.write_type(Thrift::Types::I32, 'i32')
@prot.write_type(Thrift::Types::I64, 'i64')
@prot.write_type(Thrift::Types::STRING, 'string')
@prot.write_type(Thrift::Types::STRUCT, struct)
# all other types are not implemented
[Thrift::Types::STOP, Thrift::Types::VOID, Thrift::Types::MAP, Thrift::Types::SET, Thrift::Types::LIST].each do |type|
expect { @prot.write_type(type, type.to_s) }.to raise_error(NotImplementedError)
end
end
it 'should write out the different types' do
@prot.should_receive(:write_bool).with('bool').ordered
@prot.should_receive(:write_byte).with('byte').ordered
@prot.should_receive(:write_double).with('double').ordered
@prot.should_receive(:write_i16).with('i16').ordered
@prot.should_receive(:write_i32).with('i32').ordered
@prot.should_receive(:write_i64).with('i64').ordered
@prot.should_receive(:write_string).with('string').ordered
@prot.should_receive(:write_binary).with('binary').ordered
struct = mock('Struct')
struct.should_receive(:write).with(@prot).ordered
@prot.write_type({:type => Thrift::Types::BOOL}, 'bool')
@prot.write_type({:type => Thrift::Types::BYTE}, 'byte')
@prot.write_type({:type => Thrift::Types::DOUBLE}, 'double')
@prot.write_type({:type => Thrift::Types::I16}, 'i16')
@prot.write_type({:type => Thrift::Types::I32}, 'i32')
@prot.write_type({:type => Thrift::Types::I64}, 'i64')
@prot.write_type({:type => Thrift::Types::STRING}, 'string')
@prot.write_type({:type => Thrift::Types::STRING, :binary => true}, 'binary')
@prot.write_type({:type => Thrift::Types::STRUCT}, struct)
# all other types are not implemented
[Thrift::Types::STOP, Thrift::Types::VOID, Thrift::Types::MAP, Thrift::Types::SET, Thrift::Types::LIST].each do |type|
expect { @prot.write_type({:type => type}, type.to_s) }.to raise_error(NotImplementedError)
end
end
it 'should read the different types (deprecated read_type signature)' do
@prot.should_receive(:read_bool).ordered
@prot.should_receive(:read_byte).ordered
@prot.should_receive(:read_i16).ordered
@prot.should_receive(:read_i32).ordered
@prot.should_receive(:read_i64).ordered
@prot.should_receive(:read_double).ordered
@prot.should_receive(:read_string).ordered
@prot.read_type(Thrift::Types::BOOL)
@prot.read_type(Thrift::Types::BYTE)
@prot.read_type(Thrift::Types::I16)
@prot.read_type(Thrift::Types::I32)
@prot.read_type(Thrift::Types::I64)
@prot.read_type(Thrift::Types::DOUBLE)
@prot.read_type(Thrift::Types::STRING)
# all other types are not implemented
[Thrift::Types::STOP, Thrift::Types::VOID, Thrift::Types::MAP,
Thrift::Types::SET, Thrift::Types::LIST, Thrift::Types::STRUCT].each do |type|
expect { @prot.read_type(type) }.to raise_error(NotImplementedError)
end
end
it 'should read the different types' do
@prot.should_receive(:read_bool).ordered
@prot.should_receive(:read_byte).ordered
@prot.should_receive(:read_i16).ordered
@prot.should_receive(:read_i32).ordered
@prot.should_receive(:read_i64).ordered
@prot.should_receive(:read_double).ordered
@prot.should_receive(:read_string).ordered
@prot.should_receive(:read_binary).ordered
@prot.read_type({:type => Thrift::Types::BOOL})
@prot.read_type({:type => Thrift::Types::BYTE})
@prot.read_type({:type => Thrift::Types::I16})
@prot.read_type({:type => Thrift::Types::I32})
@prot.read_type({:type => Thrift::Types::I64})
@prot.read_type({:type => Thrift::Types::DOUBLE})
@prot.read_type({:type => Thrift::Types::STRING})
@prot.read_type({:type => Thrift::Types::STRING, :binary => true})
# all other types are not implemented
[Thrift::Types::STOP, Thrift::Types::VOID, Thrift::Types::MAP,
Thrift::Types::SET, Thrift::Types::LIST, Thrift::Types::STRUCT].each do |type|
expect { @prot.read_type({:type => type}) }.to raise_error(NotImplementedError)
end
end
it "should skip the basic types" do
@prot.should_receive(:read_bool).ordered
@prot.should_receive(:read_byte).ordered
@prot.should_receive(:read_i16).ordered
@prot.should_receive(:read_i32).ordered
@prot.should_receive(:read_i64).ordered
@prot.should_receive(:read_double).ordered
@prot.should_receive(:read_string).ordered
@prot.skip(Thrift::Types::BOOL)
@prot.skip(Thrift::Types::BYTE)
@prot.skip(Thrift::Types::I16)
@prot.skip(Thrift::Types::I32)
@prot.skip(Thrift::Types::I64)
@prot.skip(Thrift::Types::DOUBLE)
@prot.skip(Thrift::Types::STRING)
@prot.skip(Thrift::Types::STOP) # should do absolutely nothing
end
it "should skip structs" do
real_skip = @prot.method(:skip)
@prot.should_receive(:read_struct_begin).ordered
@prot.should_receive(:read_field_begin).exactly(4).times.and_return(
['field 1', Thrift::Types::STRING, 1],
['field 2', Thrift::Types::I32, 2],
['field 3', Thrift::Types::MAP, 3],
[nil, Thrift::Types::STOP, 0]
)
@prot.should_receive(:read_field_end).exactly(3).times
@prot.should_receive(:read_string).exactly(3).times
@prot.should_receive(:read_i32).ordered
@prot.should_receive(:read_map_begin).ordered.and_return([Thrift::Types::STRING, Thrift::Types::STRING, 1])
# @prot.should_receive(:read_string).exactly(2).times
@prot.should_receive(:read_map_end).ordered
@prot.should_receive(:read_struct_end).ordered
real_skip.call(Thrift::Types::STRUCT)
end
it "should skip maps" do
real_skip = @prot.method(:skip)
@prot.should_receive(:read_map_begin).ordered.and_return([Thrift::Types::STRING, Thrift::Types::STRUCT, 1])
@prot.should_receive(:read_string).ordered
@prot.should_receive(:read_struct_begin).ordered.and_return(["some_struct"])
@prot.should_receive(:read_field_begin).ordered.and_return([nil, Thrift::Types::STOP, nil]);
@prot.should_receive(:read_struct_end).ordered
@prot.should_receive(:read_map_end).ordered
real_skip.call(Thrift::Types::MAP)
end
it "should skip sets" do
real_skip = @prot.method(:skip)
@prot.should_receive(:read_set_begin).ordered.and_return([Thrift::Types::I64, 9])
@prot.should_receive(:read_i64).ordered.exactly(9).times
@prot.should_receive(:read_set_end)
real_skip.call(Thrift::Types::SET)
end
it "should skip lists" do
real_skip = @prot.method(:skip)
@prot.should_receive(:read_list_begin).ordered.and_return([Thrift::Types::DOUBLE, 11])
@prot.should_receive(:read_double).ordered.exactly(11).times
@prot.should_receive(:read_list_end)
real_skip.call(Thrift::Types::LIST)
end
end
describe Thrift::BaseProtocolFactory do
it "should raise NotImplementedError" do
# returning nil since Protocol is just an abstract class
lambda {Thrift::BaseProtocolFactory.new.get_protocol(mock("MockTransport"))}.should raise_error(NotImplementedError)
end
end
end

View file

@ -0,0 +1,350 @@
#
# 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.
#
require 'spec_helper'
describe 'BaseTransport' do
describe Thrift::TransportException do
it "should make type accessible" do
exc = Thrift::TransportException.new(Thrift::TransportException::ALREADY_OPEN, "msg")
exc.type.should == Thrift::TransportException::ALREADY_OPEN
exc.message.should == "msg"
end
end
describe Thrift::BaseTransport do
it "should read the specified size" do
transport = Thrift::BaseTransport.new
transport.should_receive(:read).with(40).ordered.and_return("10 letters")
transport.should_receive(:read).with(30).ordered.and_return("fifteen letters")
transport.should_receive(:read).with(15).ordered.and_return("more characters")
transport.read_all(40).should == "10 lettersfifteen lettersmore characters"
end
it "should stub out the rest of the methods" do
# can't test for stubbiness, so just make sure they're defined
[:open?, :open, :close, :read, :write, :flush].each do |sym|
Thrift::BaseTransport.method_defined?(sym).should be_true
end
end
it "should alias << to write" do
Thrift::BaseTransport.instance_method(:<<).should == Thrift::BaseTransport.instance_method(:write)
end
end
describe Thrift::BaseServerTransport do
it "should stub out its methods" do
[:listen, :accept, :close].each do |sym|
Thrift::BaseServerTransport.method_defined?(sym).should be_true
end
end
end
describe Thrift::BaseTransportFactory do
it "should return the transport it's given" do
transport = mock("Transport")
Thrift::BaseTransportFactory.new.get_transport(transport).should eql(transport)
end
end
describe Thrift::BufferedTransport do
it "should pass through everything but write/flush/read" do
trans = mock("Transport")
trans.should_receive(:open?).ordered.and_return("+ open?")
trans.should_receive(:open).ordered.and_return("+ open")
trans.should_receive(:flush).ordered # from the close
trans.should_receive(:close).ordered.and_return("+ close")
btrans = Thrift::BufferedTransport.new(trans)
btrans.open?.should == "+ open?"
btrans.open.should == "+ open"
btrans.close.should == "+ close"
end
it "should buffer reads in chunks of #{Thrift::BufferedTransport::DEFAULT_BUFFER}" do
trans = mock("Transport")
trans.should_receive(:read).with(Thrift::BufferedTransport::DEFAULT_BUFFER).and_return("lorum ipsum dolor emet")
btrans = Thrift::BufferedTransport.new(trans)
btrans.read(6).should == "lorum "
btrans.read(6).should == "ipsum "
btrans.read(6).should == "dolor "
btrans.read(6).should == "emet"
end
it "should buffer writes and send them on flush" do
trans = mock("Transport")
btrans = Thrift::BufferedTransport.new(trans)
btrans.write("one/")
btrans.write("two/")
btrans.write("three/")
trans.should_receive(:write).with("one/two/three/").ordered
trans.should_receive(:flush).ordered
btrans.flush
end
it "should only send buffered data once" do
trans = mock("Transport")
btrans = Thrift::BufferedTransport.new(trans)
btrans.write("one/")
btrans.write("two/")
btrans.write("three/")
trans.should_receive(:write).with("one/two/three/")
trans.stub!(:flush)
btrans.flush
# Nothing to flush with no data
btrans.flush
end
it "should flush on close" do
trans = mock("Transport")
trans.should_receive(:close)
btrans = Thrift::BufferedTransport.new(trans)
btrans.should_receive(:flush)
btrans.close
end
it "should not write to socket if there's no data" do
trans = mock("Transport")
trans.should_receive(:flush)
btrans = Thrift::BufferedTransport.new(trans)
btrans.flush
end
end
describe Thrift::BufferedTransportFactory do
it "should wrap the given transport in a BufferedTransport" do
trans = mock("Transport")
btrans = mock("BufferedTransport")
Thrift::BufferedTransport.should_receive(:new).with(trans).and_return(btrans)
Thrift::BufferedTransportFactory.new.get_transport(trans).should == btrans
end
end
describe Thrift::FramedTransport do
before(:each) do
@trans = mock("Transport")
end
it "should pass through open?/open/close" do
ftrans = Thrift::FramedTransport.new(@trans)
@trans.should_receive(:open?).ordered.and_return("+ open?")
@trans.should_receive(:open).ordered.and_return("+ open")
@trans.should_receive(:close).ordered.and_return("+ close")
ftrans.open?.should == "+ open?"
ftrans.open.should == "+ open"
ftrans.close.should == "+ close"
end
it "should pass through read when read is turned off" do
ftrans = Thrift::FramedTransport.new(@trans, false, true)
@trans.should_receive(:read).with(17).ordered.and_return("+ read")
ftrans.read(17).should == "+ read"
end
it "should pass through write/flush when write is turned off" do
ftrans = Thrift::FramedTransport.new(@trans, true, false)
@trans.should_receive(:write).with("foo").ordered.and_return("+ write")
@trans.should_receive(:flush).ordered.and_return("+ flush")
ftrans.write("foo").should == "+ write"
ftrans.flush.should == "+ flush"
end
it "should return a full frame if asked for >= the frame's length" do
frame = "this is a frame"
@trans.should_receive(:read_all).with(4).and_return("\000\000\000\017")
@trans.should_receive(:read_all).with(frame.length).and_return(frame)
Thrift::FramedTransport.new(@trans).read(frame.length + 10).should == frame
end
it "should return slices of the frame when asked for < the frame's length" do
frame = "this is a frame"
@trans.should_receive(:read_all).with(4).and_return("\000\000\000\017")
@trans.should_receive(:read_all).with(frame.length).and_return(frame)
ftrans = Thrift::FramedTransport.new(@trans)
ftrans.read(4).should == "this"
ftrans.read(4).should == " is "
ftrans.read(16).should == "a frame"
end
it "should return nothing if asked for <= 0" do
Thrift::FramedTransport.new(@trans).read(-2).should == ""
end
it "should pull a new frame when the first is exhausted" do
frame = "this is a frame"
frame2 = "yet another frame"
@trans.should_receive(:read_all).with(4).and_return("\000\000\000\017", "\000\000\000\021")
@trans.should_receive(:read_all).with(frame.length).and_return(frame)
@trans.should_receive(:read_all).with(frame2.length).and_return(frame2)
ftrans = Thrift::FramedTransport.new(@trans)
ftrans.read(4).should == "this"
ftrans.read(8).should == " is a fr"
ftrans.read(6).should == "ame"
ftrans.read(4).should == "yet "
ftrans.read(16).should == "another frame"
end
it "should buffer writes" do
ftrans = Thrift::FramedTransport.new(@trans)
@trans.should_not_receive(:write)
ftrans.write("foo")
ftrans.write("bar")
ftrans.write("this is a frame")
end
it "should write slices of the buffer" do
ftrans = Thrift::FramedTransport.new(@trans)
ftrans.write("foobar", 3)
ftrans.write("barfoo", 1)
@trans.stub!(:flush)
@trans.should_receive(:write).with("\000\000\000\004foob")
ftrans.flush
end
it "should flush frames with a 4-byte header" do
ftrans = Thrift::FramedTransport.new(@trans)
@trans.should_receive(:write).with("\000\000\000\035one/two/three/this is a frame").ordered
@trans.should_receive(:flush).ordered
ftrans.write("one/")
ftrans.write("two/")
ftrans.write("three/")
ftrans.write("this is a frame")
ftrans.flush
end
it "should not flush the same buffered data twice" do
ftrans = Thrift::FramedTransport.new(@trans)
@trans.should_receive(:write).with("\000\000\000\007foo/bar")
@trans.stub!(:flush)
ftrans.write("foo")
ftrans.write("/bar")
ftrans.flush
@trans.should_receive(:write).with("\000\000\000\000")
ftrans.flush
end
end
describe Thrift::FramedTransportFactory do
it "should wrap the given transport in a FramedTransport" do
trans = mock("Transport")
Thrift::FramedTransport.should_receive(:new).with(trans)
Thrift::FramedTransportFactory.new.get_transport(trans)
end
end
describe Thrift::MemoryBufferTransport do
before(:each) do
@buffer = Thrift::MemoryBufferTransport.new
end
it "should accept a buffer on input and use it directly" do
s = "this is a test"
@buffer = Thrift::MemoryBufferTransport.new(s)
@buffer.read(4).should == "this"
s.slice!(-4..-1)
@buffer.read(@buffer.available).should == " is a "
end
it "should always remain open" do
@buffer.should be_open
@buffer.close
@buffer.should be_open
end
it "should respond to peek and available" do
@buffer.write "some data"
@buffer.peek.should be_true
@buffer.available.should == 9
@buffer.read(4)
@buffer.peek.should be_true
@buffer.available.should == 5
@buffer.read(5)
@buffer.peek.should be_false
@buffer.available.should == 0
end
it "should be able to reset the buffer" do
@buffer.write "test data"
@buffer.reset_buffer("foobar")
@buffer.available.should == 6
@buffer.read(@buffer.available).should == "foobar"
@buffer.reset_buffer
@buffer.available.should == 0
end
it "should copy the given string when resetting the buffer" do
s = "this is a test"
@buffer.reset_buffer(s)
@buffer.available.should == 14
@buffer.read(10)
@buffer.available.should == 4
s.should == "this is a test"
end
it "should return from read what was given in write" do
@buffer.write "test data"
@buffer.read(4).should == "test"
@buffer.read(@buffer.available).should == " data"
@buffer.write "foo"
@buffer.write " bar"
@buffer.read(@buffer.available).should == "foo bar"
end
it "should throw an EOFError when there isn't enough data in the buffer" do
@buffer.reset_buffer("")
lambda{@buffer.read(1)}.should raise_error(EOFError)
@buffer.reset_buffer("1234")
lambda{@buffer.read(5)}.should raise_error(EOFError)
end
end
describe Thrift::IOStreamTransport do
before(:each) do
@input = mock("Input", :closed? => false)
@output = mock("Output", :closed? => false)
@trans = Thrift::IOStreamTransport.new(@input, @output)
end
it "should be open as long as both input or output are open" do
@trans.should be_open
@input.stub!(:closed?).and_return(true)
@trans.should be_open
@input.stub!(:closed?).and_return(false)
@output.stub!(:closed?).and_return(true)
@trans.should be_open
@input.stub!(:closed?).and_return(true)
@trans.should_not be_open
end
it "should pass through read/write to input/output" do
@input.should_receive(:read).with(17).and_return("+ read")
@output.should_receive(:write).with("foobar").and_return("+ write")
@trans.read(17).should == "+ read"
@trans.write("foobar").should == "+ write"
end
it "should close both input and output when closed" do
@input.should_receive(:close)
@output.should_receive(:close)
@trans.close
end
end
end

View file

@ -0,0 +1,42 @@
#
# 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.
#
require 'spec_helper'
require File.expand_path("#{File.dirname(__FILE__)}/binary_protocol_spec_shared")
if defined? Thrift::BinaryProtocolAccelerated
describe 'BinaryProtocolAccelerated' do
# since BinaryProtocolAccelerated should be directly equivalent to
# BinaryProtocol, we don't need any custom specs!
it_should_behave_like 'a binary protocol'
def protocol_class
Thrift::BinaryProtocolAccelerated
end
describe Thrift::BinaryProtocolAcceleratedFactory do
it "should create a BinaryProtocolAccelerated" do
Thrift::BinaryProtocolAcceleratedFactory.new.get_protocol(mock("MockTransport")).should be_instance_of(Thrift::BinaryProtocolAccelerated)
end
end
end
else
puts "skipping BinaryProtocolAccelerated spec because it is not defined."
end

View file

@ -0,0 +1,66 @@
#
# 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.
#
require 'spec_helper'
require File.expand_path("#{File.dirname(__FILE__)}/binary_protocol_spec_shared")
describe 'BinaryProtocol' do
it_should_behave_like 'a binary protocol'
def protocol_class
Thrift::BinaryProtocol
end
describe Thrift::BinaryProtocol do
before(:each) do
@trans = Thrift::MemoryBufferTransport.new
@prot = protocol_class.new(@trans)
end
it "should read a message header" do
@trans.write([protocol_class.const_get(:VERSION_1) | Thrift::MessageTypes::REPLY].pack('N'))
@trans.write([42].pack('N'))
@prot.should_receive(:read_string).and_return('testMessage')
@prot.read_message_begin.should == ['testMessage', Thrift::MessageTypes::REPLY, 42]
end
it "should raise an exception if the message header has the wrong version" do
@prot.should_receive(:read_i32).and_return(-1)
lambda { @prot.read_message_begin }.should raise_error(Thrift::ProtocolException, 'Missing version identifier') do |e|
e.type == Thrift::ProtocolException::BAD_VERSION
end
end
it "should raise an exception if the message header does not exist and strict_read is enabled" do
@prot.should_receive(:read_i32).and_return(42)
@prot.should_receive(:strict_read).and_return(true)
lambda { @prot.read_message_begin }.should raise_error(Thrift::ProtocolException, 'No version identifier, old protocol client?') do |e|
e.type == Thrift::ProtocolException::BAD_VERSION
end
end
end
describe Thrift::BinaryProtocolFactory do
it "should create a BinaryProtocol" do
Thrift::BinaryProtocolFactory.new.get_protocol(mock("MockTransport")).should be_instance_of(Thrift::BinaryProtocol)
end
end
end

View file

@ -0,0 +1,455 @@
# encoding: ascii-8bit
#
# 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.
#
require 'spec_helper'
shared_examples_for 'a binary protocol' do
before(:each) do
@trans = Thrift::MemoryBufferTransport.new
@prot = protocol_class.new(@trans)
end
it "should define the proper VERSION_1, VERSION_MASK AND TYPE_MASK" do
protocol_class.const_get(:VERSION_MASK).should == 0xffff0000
protocol_class.const_get(:VERSION_1).should == 0x80010000
protocol_class.const_get(:TYPE_MASK).should == 0x000000ff
end
it "should make strict_read readable" do
@prot.strict_read.should eql(true)
end
it "should make strict_write readable" do
@prot.strict_write.should eql(true)
end
it "should write the message header" do
@prot.write_message_begin('testMessage', Thrift::MessageTypes::CALL, 17)
@trans.read(@trans.available).should == [protocol_class.const_get(:VERSION_1) | Thrift::MessageTypes::CALL, "testMessage".size, "testMessage", 17].pack("NNa11N")
end
it "should write the message header without version when writes are not strict" do
@prot = protocol_class.new(@trans, true, false) # no strict write
@prot.write_message_begin('testMessage', Thrift::MessageTypes::CALL, 17)
@trans.read(@trans.available).should == "\000\000\000\vtestMessage\001\000\000\000\021"
end
it "should write the message header with a version when writes are strict" do
@prot = protocol_class.new(@trans) # strict write
@prot.write_message_begin('testMessage', Thrift::MessageTypes::CALL, 17)
@trans.read(@trans.available).should == "\200\001\000\001\000\000\000\vtestMessage\000\000\000\021"
end
# message footer is a noop
it "should write the field header" do
@prot.write_field_begin('foo', Thrift::Types::DOUBLE, 3)
@trans.read(@trans.available).should == [Thrift::Types::DOUBLE, 3].pack("cn")
end
# field footer is a noop
it "should write the STOP field" do
@prot.write_field_stop
@trans.read(1).should == "\000"
end
it "should write the map header" do
@prot.write_map_begin(Thrift::Types::STRING, Thrift::Types::LIST, 17)
@trans.read(@trans.available).should == [Thrift::Types::STRING, Thrift::Types::LIST, 17].pack("ccN");
end
# map footer is a noop
it "should write the list header" do
@prot.write_list_begin(Thrift::Types::I16, 42)
@trans.read(@trans.available).should == [Thrift::Types::I16, 42].pack("cN")
end
# list footer is a noop
it "should write the set header" do
@prot.write_set_begin(Thrift::Types::I16, 42)
@trans.read(@trans.available).should == [Thrift::Types::I16, 42].pack("cN")
end
it "should write a bool" do
@prot.write_bool(true)
@prot.write_bool(false)
@trans.read(@trans.available).should == "\001\000"
end
it "should treat a nil bool as false" do
@prot.write_bool(nil)
@trans.read(1).should == "\000"
end
it "should write a byte" do
# byte is small enough, let's check -128..127
(-128..127).each do |i|
@prot.write_byte(i)
@trans.read(1).should == [i].pack('c')
end
# handing it numbers out of signed range should clip
@trans.rspec_verify
(128..255).each do |i|
@prot.write_byte(i)
@trans.read(1).should == [i].pack('c')
end
# and lastly, a Bignum is going to error out
lambda { @prot.write_byte(2**65) }.should raise_error(RangeError)
end
it "should error gracefully when trying to write a nil byte" do
lambda { @prot.write_byte(nil) }.should raise_error
end
it "should write an i16" do
# try a random scattering of values
# include the signed i16 minimum/maximum
[-2**15, -1024, 17, 0, -10000, 1723, 2**15-1].each do |i|
@prot.write_i16(i)
end
# and try something out of signed range, it should clip
@prot.write_i16(2**15 + 5)
@trans.read(@trans.available).should == "\200\000\374\000\000\021\000\000\330\360\006\273\177\377\200\005"
# a Bignum should error
# lambda { @prot.write_i16(2**65) }.should raise_error(RangeError)
end
it "should error gracefully when trying to write a nil i16" do
lambda { @prot.write_i16(nil) }.should raise_error
end
it "should write an i32" do
# try a random scattering of values
# include the signed i32 minimum/maximum
[-2**31, -123123, -2532, -3, 0, 2351235, 12331, 2**31-1].each do |i|
@prot.write_i32(i)
end
# try something out of signed range, it should clip
@trans.read(@trans.available).should == "\200\000\000\000" + "\377\376\037\r" + "\377\377\366\034" + "\377\377\377\375" + "\000\000\000\000" + "\000#\340\203" + "\000\0000+" + "\177\377\377\377"
[2 ** 31 + 5, 2 ** 65 + 5].each do |i|
lambda { @prot.write_i32(i) }.should raise_error(RangeError)
end
end
it "should error gracefully when trying to write a nil i32" do
lambda { @prot.write_i32(nil) }.should raise_error
end
it "should write an i64" do
# try a random scattering of values
# try the signed i64 minimum/maximum
[-2**63, -12356123612323, -23512351, -234, 0, 1231, 2351236, 12361236213, 2**63-1].each do |i|
@prot.write_i64(i)
end
# try something out of signed range, it should clip
@trans.read(@trans.available).should == ["\200\000\000\000\000\000\000\000",
"\377\377\364\303\035\244+]",
"\377\377\377\377\376\231:\341",
"\377\377\377\377\377\377\377\026",
"\000\000\000\000\000\000\000\000",
"\000\000\000\000\000\000\004\317",
"\000\000\000\000\000#\340\204",
"\000\000\000\002\340\311~\365",
"\177\377\377\377\377\377\377\377"].join("")
lambda { @prot.write_i64(2 ** 65 + 5) }.should raise_error(RangeError)
end
it "should error gracefully when trying to write a nil i64" do
lambda { @prot.write_i64(nil) }.should raise_error
end
it "should write a double" do
# try a random scattering of values, including min/max
values = [Float::MIN,-1231.15325, -123123.23, -23.23515123, 0, 12351.1325, 523.23, Float::MAX]
values.each do |f|
@prot.write_double(f)
@trans.read(@trans.available).should == [f].pack("G")
end
end
it "should error gracefully when trying to write a nil double" do
lambda { @prot.write_double(nil) }.should raise_error
end
if RUBY_VERSION >= '1.9'
it 'should write a string' do
str = 'abc'
@prot.write_string(str)
a = @trans.read(@trans.available)
a.encoding.should == Encoding::BINARY
a.unpack('C*').should == [0x00, 0x00, 0x00, 0x03, 0x61, 0x62, 0x63]
end
it 'should write a string with unicode characters' do
str = "abc \u20AC \u20AD".encode('UTF-8')
@prot.write_string(str)
a = @trans.read(@trans.available)
a.encoding.should == Encoding::BINARY
a.unpack('C*').should == [0x00, 0x00, 0x00, 0x0B, 0x61, 0x62, 0x63, 0x20,
0xE2, 0x82, 0xAC, 0x20, 0xE2, 0x82, 0xAD]
end
it 'should write should write a string with unicode characters and transcoding' do
str = "abc \u20AC".encode('ISO-8859-15')
@prot.write_string(str)
a = @trans.read(@trans.available)
a.encoding.should == Encoding::BINARY
a.unpack('C*').should == [0x00, 0x00, 0x00, 0x07, 0x61, 0x62, 0x63, 0x20, 0xE2, 0x82, 0xAC]
end
it 'should write a binary string' do
buffer = [0, 1, 2, 3].pack('C*')
@prot.write_binary(buffer)
a = @trans.read(@trans.available)
a.encoding.should == Encoding::BINARY
a.unpack('C*').should == [0x00, 0x00, 0x00, 0x04, 0x00, 0x01, 0x02, 0x03]
end
else
it 'should write a string' do
str = 'abc'
@prot.write_string(str)
a = @trans.read(@trans.available)
a.unpack('C*').should == [0x00, 0x00, 0x00, 0x03, 0x61, 0x62, 0x63]
end
it 'should write a binary string' do
buffer = [0, 1, 2, 3].pack('C*')
@prot.write_binary(buffer)
a = @trans.read(@trans.available)
a.unpack('C*').should == [0x00, 0x00, 0x00, 0x04, 0x00, 0x01, 0x02, 0x03]
end
end
it "should error gracefully when trying to write a nil string" do
lambda { @prot.write_string(nil) }.should raise_error
end
it "should write the message header without version when writes are not strict" do
@prot = protocol_class.new(@trans, true, false) # no strict write
@prot.write_message_begin('testMessage', Thrift::MessageTypes::CALL, 17)
@trans.read(@trans.available).should == "\000\000\000\vtestMessage\001\000\000\000\021"
end
it "should write the message header with a version when writes are strict" do
@prot = protocol_class.new(@trans) # strict write
@prot.write_message_begin('testMessage', Thrift::MessageTypes::CALL, 17)
@trans.read(@trans.available).should == "\200\001\000\001\000\000\000\vtestMessage\000\000\000\021"
end
# message footer is a noop
it "should read a field header" do
@trans.write([Thrift::Types::STRING, 3].pack("cn"))
@prot.read_field_begin.should == [nil, Thrift::Types::STRING, 3]
end
# field footer is a noop
it "should read a stop field" do
@trans.write([Thrift::Types::STOP].pack("c"));
@prot.read_field_begin.should == [nil, Thrift::Types::STOP, 0]
end
it "should read a map header" do
@trans.write([Thrift::Types::DOUBLE, Thrift::Types::I64, 42].pack("ccN"))
@prot.read_map_begin.should == [Thrift::Types::DOUBLE, Thrift::Types::I64, 42]
end
# map footer is a noop
it "should read a list header" do
@trans.write([Thrift::Types::STRING, 17].pack("cN"))
@prot.read_list_begin.should == [Thrift::Types::STRING, 17]
end
# list footer is a noop
it "should read a set header" do
@trans.write([Thrift::Types::STRING, 17].pack("cN"))
@prot.read_set_begin.should == [Thrift::Types::STRING, 17]
end
# set footer is a noop
it "should read a bool" do
@trans.write("\001\000");
@prot.read_bool.should == true
@prot.read_bool.should == false
end
it "should read a byte" do
[-128, -57, -3, 0, 17, 24, 127].each do |i|
@trans.write([i].pack("c"))
@prot.read_byte.should == i
end
end
it "should read an i16" do
# try a scattering of values, including min/max
[-2**15, -5237, -353, 0, 1527, 2234, 2**15-1].each do |i|
@trans.write([i].pack("n"));
@prot.read_i16.should == i
end
end
it "should read an i32" do
# try a scattering of values, including min/max
[-2**31, -235125, -6236, 0, 2351, 123123, 2**31-1].each do |i|
@trans.write([i].pack("N"))
@prot.read_i32.should == i
end
end
it "should read an i64" do
# try a scattering of values, including min/max
[-2**63, -123512312, -6346, 0, 32, 2346322323, 2**63-1].each do |i|
@trans.write([i >> 32, i & 0xFFFFFFFF].pack("NN"))
@prot.read_i64.should == i
end
end
it "should read a double" do
# try a random scattering of values, including min/max
[Float::MIN, -231231.12351, -323.233513, 0, 123.2351235, 2351235.12351235, Float::MAX].each do |f|
@trans.write([f].pack("G"));
@prot.read_double.should == f
end
end
if RUBY_VERSION >= '1.9'
it 'should read a string' do
# i32 of value 3, followed by three characters/UTF-8 bytes 'a', 'b', 'c'
buffer = [0x00, 0x00, 0x00, 0x03, 0x61, 0x62, 0x63].pack('C*')
@trans.write(buffer)
a = @prot.read_string
a.should == 'abc'.encode('UTF-8')
a.encoding.should == Encoding::UTF_8
end
it 'should read a string containing unicode characters from UTF-8 encoded buffer' do
# i32 of value 3, followed by one character U+20AC made up of three bytes
buffer = [0x00, 0x00, 0x00, 0x03, 0xE2, 0x82, 0xAC].pack('C*')
@trans.write(buffer)
a = @prot.read_string
a.should == "\u20AC".encode('UTF-8')
a.encoding.should == Encoding::UTF_8
end
it 'should read a binary string' do
buffer = [0x00, 0x00, 0x00, 0x04, 0x00, 0x01, 0x02, 0x03].pack('C*')
@trans.write(buffer)
a = @prot.read_binary
a.should == [0x00, 0x01, 0x02, 0x03].pack('C*')
a.encoding.should == Encoding::BINARY
end
else
it 'should read a string' do
# i32 of value 3, followed by three characters/UTF-8 bytes 'a', 'b', 'c'
buffer = [0x00, 0x00, 0x00, 0x03, 0x61, 0x62, 0x63].pack('C*')
@trans.write(buffer)
@prot.read_string.should == 'abc'
end
it 'should read a binary string' do
buffer = [0x00, 0x00, 0x00, 0x04, 0x00, 0x01, 0x02, 0x03].pack('C*')
@trans.write(buffer)
a = @prot.read_binary
a.should == [0x00, 0x01, 0x02, 0x03].pack('C*')
end
end
it "should perform a complete rpc with no args or return" do
srv_test(
proc {|client| client.send_voidMethod()},
proc {|client| client.recv_voidMethod.should == nil}
)
end
it "should perform a complete rpc with a primitive return type" do
srv_test(
proc {|client| client.send_primitiveMethod()},
proc {|client| client.recv_primitiveMethod.should == 1}
)
end
it "should perform a complete rpc with a struct return type" do
srv_test(
proc {|client| client.send_structMethod()},
proc {|client|
result = client.recv_structMethod
result.set_byte_map = nil
result.map_byte_map = nil
result.should == Fixtures::COMPACT_PROTOCOL_TEST_STRUCT
}
)
end
def get_socket_connection
server = Thrift::ServerSocket.new("localhost", 9090)
server.listen
clientside = Thrift::Socket.new("localhost", 9090)
clientside.open
serverside = server.accept
[clientside, serverside, server]
end
def srv_test(firstblock, secondblock)
clientside, serverside, server = get_socket_connection
clientproto = protocol_class.new(clientside)
serverproto = protocol_class.new(serverside)
processor = Thrift::Test::Srv::Processor.new(SrvHandler.new)
client = Thrift::Test::Srv::Client.new(clientproto, clientproto)
# first block
firstblock.call(client)
processor.process(serverproto, serverproto)
# second block
secondblock.call(client)
ensure
clientside.close
serverside.close
server.close
end
class SrvHandler
def voidMethod()
end
def primitiveMethod
1
end
def structMethod
Fixtures::COMPACT_PROTOCOL_TEST_STRUCT
end
end
end

View file

@ -0,0 +1,160 @@
# encoding: UTF-8
#
# 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.
#
require 'spec_helper'
describe Thrift::Bytes do
if RUBY_VERSION >= '1.9'
describe '.empty_byte_buffer' do
it 'should create an empty buffer' do
b = Thrift::Bytes.empty_byte_buffer
b.length.should == 0
b.encoding.should == Encoding::BINARY
end
it 'should create an empty buffer of given size' do
b = Thrift::Bytes.empty_byte_buffer 2
b.length.should == 2
b.getbyte(0).should == 0
b.getbyte(1).should == 0
b.encoding.should == Encoding::BINARY
end
end
describe '.force_binary_encoding' do
it 'should change encoding' do
e = 'STRING'.encode('UTF-8')
e.encoding.should_not == Encoding::BINARY
a = Thrift::Bytes.force_binary_encoding e
a.encoding.should == Encoding::BINARY
end
end
describe '.get_string_byte' do
it 'should get the byte at index' do
s = "\x41\x42"
Thrift::Bytes.get_string_byte(s, 0).should == 0x41
Thrift::Bytes.get_string_byte(s, 1).should == 0x42
end
end
describe '.set_string_byte' do
it 'should set byte value at index' do
s = "\x41\x42"
Thrift::Bytes.set_string_byte(s, 0, 0x43)
s.getbyte(0).should == 0x43
s.should == 'CB'
end
end
describe '.convert_to_utf8_byte_buffer' do
it 'should convert UTF-8 String to byte buffer' do
e = "\u20AC".encode('UTF-8') # a string with euro sign character U+20AC
e.length.should == 1
a = Thrift::Bytes.convert_to_utf8_byte_buffer e
a.encoding.should == Encoding::BINARY
a.length.should == 3
a.unpack('C*').should == [0xE2, 0x82, 0xAC]
end
it 'should convert ISO-8859-15 String to UTF-8 byte buffer' do
# Assumptions
e = "\u20AC".encode('ISO-8859-15') # a string with euro sign character U+20AC, then converted to ISO-8859-15
e.length.should == 1
e.unpack('C*').should == [0xA4] # euro sign is a different code point in ISO-8859-15
a = Thrift::Bytes.convert_to_utf8_byte_buffer e
a.encoding.should == Encoding::BINARY
a.length.should == 3
a.unpack('C*').should == [0xE2, 0x82, 0xAC]
end
end
describe '.convert_to_string' do
it 'should convert UTF-8 byte buffer to a UTF-8 String' do
e = [0xE2, 0x82, 0xAC].pack("C*")
e.encoding.should == Encoding::BINARY
a = Thrift::Bytes.convert_to_string e
a.encoding.should == Encoding::UTF_8
a.should == "\u20AC"
end
end
else # RUBY_VERSION
describe '.empty_byte_buffer' do
it 'should create an empty buffer' do
b = Thrift::Bytes.empty_byte_buffer
b.length.should == 0
end
it 'should create an empty buffer of given size' do
b = Thrift::Bytes.empty_byte_buffer 2
b.length.should == 2
b[0].should == 0
b[1].should == 0
end
end
describe '.force_binary_encoding' do
it 'should be a no-op' do
e = 'STRING'
a = Thrift::Bytes.force_binary_encoding e
a.should == e
a.should be(e)
end
end
describe '.get_string_byte' do
it 'should get the byte at index' do
s = "\x41\x42"
Thrift::Bytes.get_string_byte(s, 0).should == 0x41
Thrift::Bytes.get_string_byte(s, 1).should == 0x42
end
end
describe '.set_string_byte' do
it 'should set byte value at index' do
s = "\x41\x42"
Thrift::Bytes.set_string_byte(s, 0, 0x43)
s[0].should == 0x43
s.should == 'CB'
end
end
describe '.convert_to_utf8_byte_buffer' do
it 'should be a no-op' do
e = 'STRING'
a = Thrift::Bytes.convert_to_utf8_byte_buffer e
a.should == e
a.should be(e)
end
end
describe '.convert_to_string' do
it 'should be a no-op' do
e = 'STRING'
a = Thrift::Bytes.convert_to_string e
a.should == e
a.should be(e)
end
end
end
end

View file

@ -0,0 +1,99 @@
#
# 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.
#
require 'spec_helper'
describe 'Client' do
class ClientSpec
include Thrift::Client
end
before(:each) do
@prot = mock("MockProtocol")
@client = ClientSpec.new(@prot)
end
describe Thrift::Client do
it "should re-use iprot for oprot if not otherwise specified" do
@client.instance_variable_get(:'@iprot').should eql(@prot)
@client.instance_variable_get(:'@oprot').should eql(@prot)
end
it "should send a test message" do
@prot.should_receive(:write_message_begin).with('testMessage', Thrift::MessageTypes::CALL, 0)
mock_args = mock('#<TestMessage_args:mock>')
mock_args.should_receive(:foo=).with('foo')
mock_args.should_receive(:bar=).with(42)
mock_args.should_receive(:write).with(@prot)
@prot.should_receive(:write_message_end)
@prot.should_receive(:trans) do
mock('trans').tap do |trans|
trans.should_receive(:flush)
end
end
klass = stub("TestMessage_args", :new => mock_args)
@client.send_message('testMessage', klass, :foo => 'foo', :bar => 42)
end
it "should increment the sequence id when sending messages" do
pending "it seems sequence ids are completely ignored right now" do
@prot.should_receive(:write_message_begin).with('testMessage', Thrift::MessageTypes::CALL, 0).ordered
@prot.should_receive(:write_message_begin).with('testMessage2', Thrift::MessageTypes::CALL, 1).ordered
@prot.should_receive(:write_message_begin).with('testMessage3', Thrift::MessageTypes::CALL, 2).ordered
@prot.stub!(:write_message_end)
@prot.stub!(:trans).and_return mock("trans").as_null_object
@client.send_message('testMessage', mock("args class").as_null_object)
@client.send_message('testMessage2', mock("args class").as_null_object)
@client.send_message('testMessage3', mock("args class").as_null_object)
end
end
it "should receive a test message" do
@prot.should_receive(:read_message_begin).and_return [nil, Thrift::MessageTypes::CALL, 0]
@prot.should_receive(:read_message_end)
mock_klass = mock("#<MockClass:mock>")
mock_klass.should_receive(:read).with(@prot)
@client.receive_message(stub("MockClass", :new => mock_klass))
end
it "should handle received exceptions" do
@prot.should_receive(:read_message_begin).and_return [nil, Thrift::MessageTypes::EXCEPTION, 0]
@prot.should_receive(:read_message_end)
Thrift::ApplicationException.should_receive(:new).and_return do
StandardError.new.tap do |mock_exc|
mock_exc.should_receive(:read).with(@prot)
end
end
lambda { @client.receive_message(nil) }.should raise_error(StandardError)
end
it "should close the transport if an error occurs while sending a message" do
@prot.stub!(:write_message_begin)
@prot.should_not_receive(:write_message_end)
mock_args = mock("#<TestMessage_args:mock>")
mock_args.should_receive(:write).with(@prot).and_raise(StandardError)
trans = mock("MockTransport")
@prot.stub!(:trans).and_return(trans)
trans.should_receive(:close)
klass = mock("TestMessage_args", :new => mock_args)
lambda { @client.send_message("testMessage", klass) }.should raise_error(StandardError)
end
end
end

View file

@ -0,0 +1,143 @@
# encoding: UTF-8
#
# 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.
#
require 'spec_helper'
describe Thrift::CompactProtocol do
TESTS = {
:byte => (-127..127).to_a,
:i16 => (0..14).map {|shift| [1 << shift, -(1 << shift)]}.flatten.sort,
:i32 => (0..30).map {|shift| [1 << shift, -(1 << shift)]}.flatten.sort,
:i64 => (0..62).map {|shift| [1 << shift, -(1 << shift)]}.flatten.sort,
:string => ["", "1", "short", "fourteen123456", "fifteen12345678", "unicode characters: \u20AC \u20AD", "1" * 127, "1" * 3000],
:binary => ["", "\001", "\001" * 5, "\001" * 14, "\001" * 15, "\001" * 127, "\001" * 3000],
:double => [0.0, 1.0, -1.0, 1.1, -1.1, 10000000.1, 1.0/0.0, -1.0/0.0],
:bool => [true, false]
}
it "should encode and decode naked primitives correctly" do
TESTS.each_pair do |primitive_type, test_values|
test_values.each do |value|
# puts "testing #{value}" if primitive_type == :i64
trans = Thrift::MemoryBufferTransport.new
proto = Thrift::CompactProtocol.new(trans)
proto.send(writer(primitive_type), value)
# puts "buf: #{trans.inspect_buffer}" if primitive_type == :i64
read_back = proto.send(reader(primitive_type))
read_back.should == value
end
end
end
it "should encode and decode primitives in fields correctly" do
TESTS.each_pair do |primitive_type, test_values|
final_primitive_type = primitive_type == :binary ? :string : primitive_type
thrift_type = Thrift::Types.const_get(final_primitive_type.to_s.upcase)
# puts primitive_type
test_values.each do |value|
trans = Thrift::MemoryBufferTransport.new
proto = Thrift::CompactProtocol.new(trans)
proto.write_field_begin(nil, thrift_type, 15)
proto.send(writer(primitive_type), value)
proto.write_field_end
proto = Thrift::CompactProtocol.new(trans)
name, type, id = proto.read_field_begin
type.should == thrift_type
id.should == 15
read_back = proto.send(reader(primitive_type))
read_back.should == value
proto.read_field_end
end
end
end
it "should encode and decode a monster struct correctly" do
trans = Thrift::MemoryBufferTransport.new
proto = Thrift::CompactProtocol.new(trans)
struct = Thrift::Test::CompactProtoTestStruct.new
# sets and maps don't hash well... not sure what to do here.
struct.write(proto)
struct2 = Thrift::Test::CompactProtoTestStruct.new
struct2.read(proto)
struct2.should == struct
end
it "should make method calls correctly" do
client_out_trans = Thrift::MemoryBufferTransport.new
client_out_proto = Thrift::CompactProtocol.new(client_out_trans)
client_in_trans = Thrift::MemoryBufferTransport.new
client_in_proto = Thrift::CompactProtocol.new(client_in_trans)
processor = Thrift::Test::Srv::Processor.new(JankyHandler.new)
client = Thrift::Test::Srv::Client.new(client_in_proto, client_out_proto)
client.send_Janky(1)
# puts client_out_trans.inspect_buffer
processor.process(client_out_proto, client_in_proto)
client.recv_Janky.should == 2
end
it "should deal with fields following fields that have non-delta ids" do
brcp = Thrift::Test::BreaksRubyCompactProtocol.new(
:field1 => "blah",
:field2 => Thrift::Test::BigFieldIdStruct.new(
:field1 => "string1",
:field2 => "string2"),
:field3 => 3)
ser = Thrift::Serializer.new(Thrift::CompactProtocolFactory.new)
bytes = ser.serialize(brcp)
deser = Thrift::Deserializer.new(Thrift::CompactProtocolFactory.new)
brcp2 = Thrift::Test::BreaksRubyCompactProtocol.new
deser.deserialize(brcp2, bytes)
brcp2.should == brcp
end
it "should deserialize an empty map to an empty hash" do
struct = Thrift::Test::SingleMapTestStruct.new(:i32_map => {})
ser = Thrift::Serializer.new(Thrift::CompactProtocolFactory.new)
bytes = ser.serialize(struct)
deser = Thrift::Deserializer.new(Thrift::CompactProtocolFactory.new)
struct2 = Thrift::Test::SingleMapTestStruct.new
deser.deserialize(struct2, bytes)
struct.should == struct2
end
class JankyHandler
def Janky(i32arg)
i32arg * 2
end
end
def writer(sym)
"write_#{sym.to_s}"
end
def reader(sym)
"read_#{sym.to_s}"
end
end

View file

@ -0,0 +1,141 @@
#
# 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.
#
require 'spec_helper'
describe 'Exception' do
describe Thrift::Exception do
it "should have an accessible message" do
e = Thrift::Exception.new("test message")
e.message.should == "test message"
end
end
describe Thrift::ApplicationException do
it "should inherit from Thrift::Exception" do
Thrift::ApplicationException.superclass.should == Thrift::Exception
end
it "should have an accessible type and message" do
e = Thrift::ApplicationException.new
e.type.should == Thrift::ApplicationException::UNKNOWN
e.message.should be_nil
e = Thrift::ApplicationException.new(Thrift::ApplicationException::UNKNOWN_METHOD, "test message")
e.type.should == Thrift::ApplicationException::UNKNOWN_METHOD
e.message.should == "test message"
end
it "should read a struct off of a protocol" do
prot = mock("MockProtocol")
prot.should_receive(:read_struct_begin).ordered
prot.should_receive(:read_field_begin).exactly(3).times.and_return(
["message", Thrift::Types::STRING, 1],
["type", Thrift::Types::I32, 2],
[nil, Thrift::Types::STOP, 0]
)
prot.should_receive(:read_string).ordered.and_return "test message"
prot.should_receive(:read_i32).ordered.and_return Thrift::ApplicationException::BAD_SEQUENCE_ID
prot.should_receive(:read_field_end).exactly(2).times
prot.should_receive(:read_struct_end).ordered
e = Thrift::ApplicationException.new
e.read(prot)
e.message.should == "test message"
e.type.should == Thrift::ApplicationException::BAD_SEQUENCE_ID
end
it "should skip bad fields when reading a struct" do
prot = mock("MockProtocol")
prot.should_receive(:read_struct_begin).ordered
prot.should_receive(:read_field_begin).exactly(5).times.and_return(
["type", Thrift::Types::I32, 2],
["type", Thrift::Types::STRING, 2],
["message", Thrift::Types::MAP, 1],
["message", Thrift::Types::STRING, 3],
[nil, Thrift::Types::STOP, 0]
)
prot.should_receive(:read_i32).and_return Thrift::ApplicationException::INVALID_MESSAGE_TYPE
prot.should_receive(:skip).with(Thrift::Types::STRING).twice
prot.should_receive(:skip).with(Thrift::Types::MAP)
prot.should_receive(:read_field_end).exactly(4).times
prot.should_receive(:read_struct_end).ordered
e = Thrift::ApplicationException.new
e.read(prot)
e.message.should be_nil
e.type.should == Thrift::ApplicationException::INVALID_MESSAGE_TYPE
end
it "should write a Thrift::ApplicationException struct to the oprot" do
prot = mock("MockProtocol")
prot.should_receive(:write_struct_begin).with("Thrift::ApplicationException").ordered
prot.should_receive(:write_field_begin).with("message", Thrift::Types::STRING, 1).ordered
prot.should_receive(:write_string).with("test message").ordered
prot.should_receive(:write_field_begin).with("type", Thrift::Types::I32, 2).ordered
prot.should_receive(:write_i32).with(Thrift::ApplicationException::UNKNOWN_METHOD).ordered
prot.should_receive(:write_field_end).twice
prot.should_receive(:write_field_stop).ordered
prot.should_receive(:write_struct_end).ordered
e = Thrift::ApplicationException.new(Thrift::ApplicationException::UNKNOWN_METHOD, "test message")
e.write(prot)
end
it "should skip nil fields when writing to the oprot" do
prot = mock("MockProtocol")
prot.should_receive(:write_struct_begin).with("Thrift::ApplicationException").ordered
prot.should_receive(:write_field_begin).with("message", Thrift::Types::STRING, 1).ordered
prot.should_receive(:write_string).with("test message").ordered
prot.should_receive(:write_field_end).ordered
prot.should_receive(:write_field_stop).ordered
prot.should_receive(:write_struct_end).ordered
e = Thrift::ApplicationException.new(nil, "test message")
e.write(prot)
prot = mock("MockProtocol")
prot.should_receive(:write_struct_begin).with("Thrift::ApplicationException").ordered
prot.should_receive(:write_field_begin).with("type", Thrift::Types::I32, 2).ordered
prot.should_receive(:write_i32).with(Thrift::ApplicationException::BAD_SEQUENCE_ID).ordered
prot.should_receive(:write_field_end).ordered
prot.should_receive(:write_field_stop).ordered
prot.should_receive(:write_struct_end).ordered
e = Thrift::ApplicationException.new(Thrift::ApplicationException::BAD_SEQUENCE_ID)
e.write(prot)
prot = mock("MockProtocol")
prot.should_receive(:write_struct_begin).with("Thrift::ApplicationException").ordered
prot.should_receive(:write_field_stop).ordered
prot.should_receive(:write_struct_end).ordered
e = Thrift::ApplicationException.new(nil)
e.write(prot)
end
end
describe Thrift::ProtocolException do
it "should have an accessible type" do
prot = Thrift::ProtocolException.new(Thrift::ProtocolException::SIZE_LIMIT, "message")
prot.type.should == Thrift::ProtocolException::SIZE_LIMIT
prot.message.should == "message"
end
end
end

View file

@ -0,0 +1,62 @@
#
# 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.
#
require 'spec_helper'
describe 'generation' do
before do
require 'namespaced_nonblocking_service'
end
it "did not generate the wrong files" do
prefix = File.expand_path("../gen-rb/flat", __FILE__)
["namespaced_spec_namespace/namespaced_nonblocking_service.rb",
"namespaced_spec_namespace/thrift_namespaced_spec_constants.rb",
"namespaced_spec_namespace/thrift_namespaced_spec_types.rb",
"other_namespace/referenced_constants.rb",
"other_namespace/referenced_types.rb"
].each do |name|
File.exist?(File.join(prefix, name)).should_not be_true
end
end
it "generated the right files" do
prefix = File.expand_path("../gen-rb/flat", __FILE__)
["namespaced_nonblocking_service.rb",
"thrift_namespaced_spec_constants.rb",
"thrift_namespaced_spec_types.rb",
"referenced_constants.rb",
"referenced_types.rb"
].each do |name|
File.exist?(File.join(prefix, name)).should be_true
end
end
it "has a service class in the right place" do
defined?(NamespacedSpecNamespace::NamespacedNonblockingService).should be_true
end
it "has a struct in the right place" do
defined?(NamespacedSpecNamespace::Hello).should be_true
end
it "required an included file" do
defined?(OtherNamespace::SomeEnum).should be_true
end
end

View file

@ -0,0 +1,80 @@
#
# Autogenerated by Thrift Compiler (0.11.0)
#
# DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
#
require 'thrift'
require 'base/base_service_types'
module Base
module BaseService
class Client
include ::Thrift::Client
def greeting(english)
send_greeting(english)
return recv_greeting()
end
def send_greeting(english)
send_message('greeting', Greeting_args, :english => english)
end
def recv_greeting()
result = receive_message(Greeting_result)
return result.success unless result.success.nil?
raise ::Thrift::ApplicationException.new(::Thrift::ApplicationException::MISSING_RESULT, 'greeting failed: unknown result')
end
end
class Processor
include ::Thrift::Processor
def process_greeting(seqid, iprot, oprot)
args = read_args(iprot, Greeting_args)
result = Greeting_result.new()
result.success = @handler.greeting(args.english)
write_result(result, oprot, 'greeting', seqid)
end
end
# HELPER FUNCTIONS AND STRUCTURES
class Greeting_args
include ::Thrift::Struct, ::Thrift::Struct_Union
ENGLISH = 1
FIELDS = {
ENGLISH => {:type => ::Thrift::Types::BOOL, :name => 'english'}
}
def struct_fields; FIELDS; end
def validate
end
::Thrift::Struct.generate_accessors self
end
class Greeting_result
include ::Thrift::Struct, ::Thrift::Struct_Union
SUCCESS = 0
FIELDS = {
SUCCESS => {:type => ::Thrift::Types::STRUCT, :name => 'success', :class => ::Base::Hello}
}
def struct_fields; FIELDS; end
def validate
end
::Thrift::Struct.generate_accessors self
end
end
end

View file

@ -0,0 +1,11 @@
#
# Autogenerated by Thrift Compiler (0.11.0)
#
# DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
#
require 'thrift'
require 'base/base_service_types'
module Base
end

View file

@ -0,0 +1,26 @@
#
# Autogenerated by Thrift Compiler (0.11.0)
#
# DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
#
require 'thrift'
module Base
class Hello
include ::Thrift::Struct, ::Thrift::Struct_Union
GREETING = 1
FIELDS = {
GREETING => {:type => ::Thrift::Types::STRING, :name => 'greeting', :default => %q"hello world"}
}
def struct_fields; FIELDS; end
def validate
end
::Thrift::Struct.generate_accessors self
end
end

View file

@ -0,0 +1,78 @@
#
# Autogenerated by Thrift Compiler (0.11.0)
#
# DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
#
require 'thrift'
require 'base/base_service'
require 'extended/extended_service_types'
module Extended
module ExtendedService
class Client < ::Base::BaseService::Client
include ::Thrift::Client
def ping()
send_ping()
recv_ping()
end
def send_ping()
send_message('ping', Ping_args)
end
def recv_ping()
result = receive_message(Ping_result)
return
end
end
class Processor < ::Base::BaseService::Processor
include ::Thrift::Processor
def process_ping(seqid, iprot, oprot)
args = read_args(iprot, Ping_args)
result = Ping_result.new()
@handler.ping()
write_result(result, oprot, 'ping', seqid)
end
end
# HELPER FUNCTIONS AND STRUCTURES
class Ping_args
include ::Thrift::Struct, ::Thrift::Struct_Union
FIELDS = {
}
def struct_fields; FIELDS; end
def validate
end
::Thrift::Struct.generate_accessors self
end
class Ping_result
include ::Thrift::Struct, ::Thrift::Struct_Union
FIELDS = {
}
def struct_fields; FIELDS; end
def validate
end
::Thrift::Struct.generate_accessors self
end
end
end

View file

@ -0,0 +1,11 @@
#
# Autogenerated by Thrift Compiler (0.11.0)
#
# DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
#
require 'thrift'
require 'extended/extended_service_types'
module Extended
end

View file

@ -0,0 +1,12 @@
#
# Autogenerated by Thrift Compiler (0.11.0)
#
# DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
#
require 'thrift'
require 'base/base_service_types'
module Extended
end

View file

@ -0,0 +1,272 @@
#
# Autogenerated by Thrift Compiler (0.11.0)
#
# DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
#
require 'thrift'
require 'thrift_namespaced_spec_types'
module NamespacedSpecNamespace
module NamespacedNonblockingService
class Client
include ::Thrift::Client
def greeting(english)
send_greeting(english)
return recv_greeting()
end
def send_greeting(english)
send_message('greeting', Greeting_args, :english => english)
end
def recv_greeting()
result = receive_message(Greeting_result)
return result.success unless result.success.nil?
raise ::Thrift::ApplicationException.new(::Thrift::ApplicationException::MISSING_RESULT, 'greeting failed: unknown result')
end
def block()
send_block()
return recv_block()
end
def send_block()
send_message('block', Block_args)
end
def recv_block()
result = receive_message(Block_result)
return result.success unless result.success.nil?
raise ::Thrift::ApplicationException.new(::Thrift::ApplicationException::MISSING_RESULT, 'block failed: unknown result')
end
def unblock(n)
send_unblock(n)
end
def send_unblock(n)
send_oneway_message('unblock', Unblock_args, :n => n)
end
def shutdown()
send_shutdown()
end
def send_shutdown()
send_oneway_message('shutdown', Shutdown_args)
end
def sleep(seconds)
send_sleep(seconds)
recv_sleep()
end
def send_sleep(seconds)
send_message('sleep', Sleep_args, :seconds => seconds)
end
def recv_sleep()
result = receive_message(Sleep_result)
return
end
end
class Processor
include ::Thrift::Processor
def process_greeting(seqid, iprot, oprot)
args = read_args(iprot, Greeting_args)
result = Greeting_result.new()
result.success = @handler.greeting(args.english)
write_result(result, oprot, 'greeting', seqid)
end
def process_block(seqid, iprot, oprot)
args = read_args(iprot, Block_args)
result = Block_result.new()
result.success = @handler.block()
write_result(result, oprot, 'block', seqid)
end
def process_unblock(seqid, iprot, oprot)
args = read_args(iprot, Unblock_args)
@handler.unblock(args.n)
return
end
def process_shutdown(seqid, iprot, oprot)
args = read_args(iprot, Shutdown_args)
@handler.shutdown()
return
end
def process_sleep(seqid, iprot, oprot)
args = read_args(iprot, Sleep_args)
result = Sleep_result.new()
@handler.sleep(args.seconds)
write_result(result, oprot, 'sleep', seqid)
end
end
# HELPER FUNCTIONS AND STRUCTURES
class Greeting_args
include ::Thrift::Struct, ::Thrift::Struct_Union
ENGLISH = 1
FIELDS = {
ENGLISH => {:type => ::Thrift::Types::BOOL, :name => 'english'}
}
def struct_fields; FIELDS; end
def validate
end
::Thrift::Struct.generate_accessors self
end
class Greeting_result
include ::Thrift::Struct, ::Thrift::Struct_Union
SUCCESS = 0
FIELDS = {
SUCCESS => {:type => ::Thrift::Types::STRUCT, :name => 'success', :class => ::NamespacedSpecNamespace::Hello}
}
def struct_fields; FIELDS; end
def validate
end
::Thrift::Struct.generate_accessors self
end
class Block_args
include ::Thrift::Struct, ::Thrift::Struct_Union
FIELDS = {
}
def struct_fields; FIELDS; end
def validate
end
::Thrift::Struct.generate_accessors self
end
class Block_result
include ::Thrift::Struct, ::Thrift::Struct_Union
SUCCESS = 0
FIELDS = {
SUCCESS => {:type => ::Thrift::Types::BOOL, :name => 'success'}
}
def struct_fields; FIELDS; end
def validate
end
::Thrift::Struct.generate_accessors self
end
class Unblock_args
include ::Thrift::Struct, ::Thrift::Struct_Union
N = 1
FIELDS = {
N => {:type => ::Thrift::Types::I32, :name => 'n'}
}
def struct_fields; FIELDS; end
def validate
end
::Thrift::Struct.generate_accessors self
end
class Unblock_result
include ::Thrift::Struct, ::Thrift::Struct_Union
FIELDS = {
}
def struct_fields; FIELDS; end
def validate
end
::Thrift::Struct.generate_accessors self
end
class Shutdown_args
include ::Thrift::Struct, ::Thrift::Struct_Union
FIELDS = {
}
def struct_fields; FIELDS; end
def validate
end
::Thrift::Struct.generate_accessors self
end
class Shutdown_result
include ::Thrift::Struct, ::Thrift::Struct_Union
FIELDS = {
}
def struct_fields; FIELDS; end
def validate
end
::Thrift::Struct.generate_accessors self
end
class Sleep_args
include ::Thrift::Struct, ::Thrift::Struct_Union
SECONDS = 1
FIELDS = {
SECONDS => {:type => ::Thrift::Types::DOUBLE, :name => 'seconds'}
}
def struct_fields; FIELDS; end
def validate
end
::Thrift::Struct.generate_accessors self
end
class Sleep_result
include ::Thrift::Struct, ::Thrift::Struct_Union
FIELDS = {
}
def struct_fields; FIELDS; end
def validate
end
::Thrift::Struct.generate_accessors self
end
end
end

View file

@ -0,0 +1,11 @@
#
# Autogenerated by Thrift Compiler (0.11.0)
#
# DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
#
require 'thrift'
require 'referenced_types'
module OtherNamespace
end

View file

@ -0,0 +1,17 @@
#
# Autogenerated by Thrift Compiler (0.11.0)
#
# DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
#
require 'thrift'
module OtherNamespace
module SomeEnum
ONE = 0
TWO = 1
VALUE_MAP = {0 => "ONE", 1 => "TWO"}
VALID_VALUES = Set.new([ONE, TWO]).freeze
end
end

View file

@ -0,0 +1,11 @@
#
# Autogenerated by Thrift Compiler (0.11.0)
#
# DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
#
require 'thrift'
require 'thrift_namespaced_spec_types'
module NamespacedSpecNamespace
end

View file

@ -0,0 +1,28 @@
#
# Autogenerated by Thrift Compiler (0.11.0)
#
# DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
#
require 'thrift'
require 'referenced_types'
module NamespacedSpecNamespace
class Hello
include ::Thrift::Struct, ::Thrift::Struct_Union
GREETING = 1
FIELDS = {
GREETING => {:type => ::Thrift::Types::STRING, :name => 'greeting', :default => %q"hello world"}
}
def struct_fields; FIELDS; end
def validate
end
::Thrift::Struct.generate_accessors self
end
end

View file

@ -0,0 +1,272 @@
#
# Autogenerated by Thrift Compiler (0.11.0)
#
# DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
#
require 'thrift'
require 'namespaced_spec_namespace/thrift_namespaced_spec_types'
module NamespacedSpecNamespace
module NamespacedNonblockingService
class Client
include ::Thrift::Client
def greeting(english)
send_greeting(english)
return recv_greeting()
end
def send_greeting(english)
send_message('greeting', Greeting_args, :english => english)
end
def recv_greeting()
result = receive_message(Greeting_result)
return result.success unless result.success.nil?
raise ::Thrift::ApplicationException.new(::Thrift::ApplicationException::MISSING_RESULT, 'greeting failed: unknown result')
end
def block()
send_block()
return recv_block()
end
def send_block()
send_message('block', Block_args)
end
def recv_block()
result = receive_message(Block_result)
return result.success unless result.success.nil?
raise ::Thrift::ApplicationException.new(::Thrift::ApplicationException::MISSING_RESULT, 'block failed: unknown result')
end
def unblock(n)
send_unblock(n)
end
def send_unblock(n)
send_oneway_message('unblock', Unblock_args, :n => n)
end
def shutdown()
send_shutdown()
end
def send_shutdown()
send_oneway_message('shutdown', Shutdown_args)
end
def sleep(seconds)
send_sleep(seconds)
recv_sleep()
end
def send_sleep(seconds)
send_message('sleep', Sleep_args, :seconds => seconds)
end
def recv_sleep()
result = receive_message(Sleep_result)
return
end
end
class Processor
include ::Thrift::Processor
def process_greeting(seqid, iprot, oprot)
args = read_args(iprot, Greeting_args)
result = Greeting_result.new()
result.success = @handler.greeting(args.english)
write_result(result, oprot, 'greeting', seqid)
end
def process_block(seqid, iprot, oprot)
args = read_args(iprot, Block_args)
result = Block_result.new()
result.success = @handler.block()
write_result(result, oprot, 'block', seqid)
end
def process_unblock(seqid, iprot, oprot)
args = read_args(iprot, Unblock_args)
@handler.unblock(args.n)
return
end
def process_shutdown(seqid, iprot, oprot)
args = read_args(iprot, Shutdown_args)
@handler.shutdown()
return
end
def process_sleep(seqid, iprot, oprot)
args = read_args(iprot, Sleep_args)
result = Sleep_result.new()
@handler.sleep(args.seconds)
write_result(result, oprot, 'sleep', seqid)
end
end
# HELPER FUNCTIONS AND STRUCTURES
class Greeting_args
include ::Thrift::Struct, ::Thrift::Struct_Union
ENGLISH = 1
FIELDS = {
ENGLISH => {:type => ::Thrift::Types::BOOL, :name => 'english'}
}
def struct_fields; FIELDS; end
def validate
end
::Thrift::Struct.generate_accessors self
end
class Greeting_result
include ::Thrift::Struct, ::Thrift::Struct_Union
SUCCESS = 0
FIELDS = {
SUCCESS => {:type => ::Thrift::Types::STRUCT, :name => 'success', :class => ::NamespacedSpecNamespace::Hello}
}
def struct_fields; FIELDS; end
def validate
end
::Thrift::Struct.generate_accessors self
end
class Block_args
include ::Thrift::Struct, ::Thrift::Struct_Union
FIELDS = {
}
def struct_fields; FIELDS; end
def validate
end
::Thrift::Struct.generate_accessors self
end
class Block_result
include ::Thrift::Struct, ::Thrift::Struct_Union
SUCCESS = 0
FIELDS = {
SUCCESS => {:type => ::Thrift::Types::BOOL, :name => 'success'}
}
def struct_fields; FIELDS; end
def validate
end
::Thrift::Struct.generate_accessors self
end
class Unblock_args
include ::Thrift::Struct, ::Thrift::Struct_Union
N = 1
FIELDS = {
N => {:type => ::Thrift::Types::I32, :name => 'n'}
}
def struct_fields; FIELDS; end
def validate
end
::Thrift::Struct.generate_accessors self
end
class Unblock_result
include ::Thrift::Struct, ::Thrift::Struct_Union
FIELDS = {
}
def struct_fields; FIELDS; end
def validate
end
::Thrift::Struct.generate_accessors self
end
class Shutdown_args
include ::Thrift::Struct, ::Thrift::Struct_Union
FIELDS = {
}
def struct_fields; FIELDS; end
def validate
end
::Thrift::Struct.generate_accessors self
end
class Shutdown_result
include ::Thrift::Struct, ::Thrift::Struct_Union
FIELDS = {
}
def struct_fields; FIELDS; end
def validate
end
::Thrift::Struct.generate_accessors self
end
class Sleep_args
include ::Thrift::Struct, ::Thrift::Struct_Union
SECONDS = 1
FIELDS = {
SECONDS => {:type => ::Thrift::Types::DOUBLE, :name => 'seconds'}
}
def struct_fields; FIELDS; end
def validate
end
::Thrift::Struct.generate_accessors self
end
class Sleep_result
include ::Thrift::Struct, ::Thrift::Struct_Union
FIELDS = {
}
def struct_fields; FIELDS; end
def validate
end
::Thrift::Struct.generate_accessors self
end
end
end

View file

@ -0,0 +1,11 @@
#
# Autogenerated by Thrift Compiler (0.11.0)
#
# DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
#
require 'thrift'
require 'namespaced_spec_namespace/thrift_namespaced_spec_types'
module NamespacedSpecNamespace
end

View file

@ -0,0 +1,28 @@
#
# Autogenerated by Thrift Compiler (0.11.0)
#
# DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
#
require 'thrift'
require 'other_namespace/referenced_types'
module NamespacedSpecNamespace
class Hello
include ::Thrift::Struct, ::Thrift::Struct_Union
GREETING = 1
FIELDS = {
GREETING => {:type => ::Thrift::Types::STRING, :name => 'greeting', :default => %q"hello world"}
}
def struct_fields; FIELDS; end
def validate
end
::Thrift::Struct.generate_accessors self
end
end

View file

@ -0,0 +1,272 @@
#
# Autogenerated by Thrift Compiler (0.11.0)
#
# DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
#
require 'thrift'
require 'thrift_spec_types'
module SpecNamespace
module NonblockingService
class Client
include ::Thrift::Client
def greeting(english)
send_greeting(english)
return recv_greeting()
end
def send_greeting(english)
send_message('greeting', Greeting_args, :english => english)
end
def recv_greeting()
result = receive_message(Greeting_result)
return result.success unless result.success.nil?
raise ::Thrift::ApplicationException.new(::Thrift::ApplicationException::MISSING_RESULT, 'greeting failed: unknown result')
end
def block()
send_block()
return recv_block()
end
def send_block()
send_message('block', Block_args)
end
def recv_block()
result = receive_message(Block_result)
return result.success unless result.success.nil?
raise ::Thrift::ApplicationException.new(::Thrift::ApplicationException::MISSING_RESULT, 'block failed: unknown result')
end
def unblock(n)
send_unblock(n)
end
def send_unblock(n)
send_oneway_message('unblock', Unblock_args, :n => n)
end
def shutdown()
send_shutdown()
end
def send_shutdown()
send_oneway_message('shutdown', Shutdown_args)
end
def sleep(seconds)
send_sleep(seconds)
recv_sleep()
end
def send_sleep(seconds)
send_message('sleep', Sleep_args, :seconds => seconds)
end
def recv_sleep()
result = receive_message(Sleep_result)
return
end
end
class Processor
include ::Thrift::Processor
def process_greeting(seqid, iprot, oprot)
args = read_args(iprot, Greeting_args)
result = Greeting_result.new()
result.success = @handler.greeting(args.english)
write_result(result, oprot, 'greeting', seqid)
end
def process_block(seqid, iprot, oprot)
args = read_args(iprot, Block_args)
result = Block_result.new()
result.success = @handler.block()
write_result(result, oprot, 'block', seqid)
end
def process_unblock(seqid, iprot, oprot)
args = read_args(iprot, Unblock_args)
@handler.unblock(args.n)
return
end
def process_shutdown(seqid, iprot, oprot)
args = read_args(iprot, Shutdown_args)
@handler.shutdown()
return
end
def process_sleep(seqid, iprot, oprot)
args = read_args(iprot, Sleep_args)
result = Sleep_result.new()
@handler.sleep(args.seconds)
write_result(result, oprot, 'sleep', seqid)
end
end
# HELPER FUNCTIONS AND STRUCTURES
class Greeting_args
include ::Thrift::Struct, ::Thrift::Struct_Union
ENGLISH = 1
FIELDS = {
ENGLISH => {:type => ::Thrift::Types::BOOL, :name => 'english'}
}
def struct_fields; FIELDS; end
def validate
end
::Thrift::Struct.generate_accessors self
end
class Greeting_result
include ::Thrift::Struct, ::Thrift::Struct_Union
SUCCESS = 0
FIELDS = {
SUCCESS => {:type => ::Thrift::Types::STRUCT, :name => 'success', :class => ::SpecNamespace::Hello}
}
def struct_fields; FIELDS; end
def validate
end
::Thrift::Struct.generate_accessors self
end
class Block_args
include ::Thrift::Struct, ::Thrift::Struct_Union
FIELDS = {
}
def struct_fields; FIELDS; end
def validate
end
::Thrift::Struct.generate_accessors self
end
class Block_result
include ::Thrift::Struct, ::Thrift::Struct_Union
SUCCESS = 0
FIELDS = {
SUCCESS => {:type => ::Thrift::Types::BOOL, :name => 'success'}
}
def struct_fields; FIELDS; end
def validate
end
::Thrift::Struct.generate_accessors self
end
class Unblock_args
include ::Thrift::Struct, ::Thrift::Struct_Union
N = 1
FIELDS = {
N => {:type => ::Thrift::Types::I32, :name => 'n'}
}
def struct_fields; FIELDS; end
def validate
end
::Thrift::Struct.generate_accessors self
end
class Unblock_result
include ::Thrift::Struct, ::Thrift::Struct_Union
FIELDS = {
}
def struct_fields; FIELDS; end
def validate
end
::Thrift::Struct.generate_accessors self
end
class Shutdown_args
include ::Thrift::Struct, ::Thrift::Struct_Union
FIELDS = {
}
def struct_fields; FIELDS; end
def validate
end
::Thrift::Struct.generate_accessors self
end
class Shutdown_result
include ::Thrift::Struct, ::Thrift::Struct_Union
FIELDS = {
}
def struct_fields; FIELDS; end
def validate
end
::Thrift::Struct.generate_accessors self
end
class Sleep_args
include ::Thrift::Struct, ::Thrift::Struct_Union
SECONDS = 1
FIELDS = {
SECONDS => {:type => ::Thrift::Types::DOUBLE, :name => 'seconds'}
}
def struct_fields; FIELDS; end
def validate
end
::Thrift::Struct.generate_accessors self
end
class Sleep_result
include ::Thrift::Struct, ::Thrift::Struct_Union
FIELDS = {
}
def struct_fields; FIELDS; end
def validate
end
::Thrift::Struct.generate_accessors self
end
end
end

View file

@ -0,0 +1,11 @@
#
# Autogenerated by Thrift Compiler (0.11.0)
#
# DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
#
require 'thrift'
require 'other_namespace/referenced_types'
module OtherNamespace
end

Some files were not shown because too many files have changed in this diff Show more