summaryrefslogtreecommitdiffstats
path: root/src/jaegertracing/thrift/lib/rb
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 18:45:59 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 18:45:59 +0000
commit19fcec84d8d7d21e796c7624e521b60d28ee21ed (patch)
tree42d26aa27d1e3f7c0b8bd3fd14e7d7082f5008dc /src/jaegertracing/thrift/lib/rb
parentInitial commit. (diff)
downloadceph-19fcec84d8d7d21e796c7624e521b60d28ee21ed.tar.xz
ceph-19fcec84d8d7d21e796c7624e521b60d28ee21ed.zip
Adding upstream version 16.2.11+ds.upstream/16.2.11+dsupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/jaegertracing/thrift/lib/rb')
-rw-r--r--src/jaegertracing/thrift/lib/rb/Gemfile4
-rwxr-xr-xsrc/jaegertracing/thrift/lib/rb/Makefile.am55
-rw-r--r--src/jaegertracing/thrift/lib/rb/README.md43
-rw-r--r--src/jaegertracing/thrift/lib/rb/Rakefile120
-rw-r--r--src/jaegertracing/thrift/lib/rb/benchmark/Benchmark.thrift24
-rw-r--r--src/jaegertracing/thrift/lib/rb/benchmark/benchmark.rb271
-rw-r--r--src/jaegertracing/thrift/lib/rb/benchmark/client.rb74
-rw-r--r--src/jaegertracing/thrift/lib/rb/benchmark/server.rb82
-rw-r--r--src/jaegertracing/thrift/lib/rb/benchmark/thin_server.rb44
-rw-r--r--src/jaegertracing/thrift/lib/rb/coding_standards.md1
-rw-r--r--src/jaegertracing/thrift/lib/rb/ext/binary_protocol_accelerated.c460
-rw-r--r--src/jaegertracing/thrift/lib/rb/ext/binary_protocol_accelerated.h20
-rw-r--r--src/jaegertracing/thrift/lib/rb/ext/bytes.c36
-rw-r--r--src/jaegertracing/thrift/lib/rb/ext/bytes.h31
-rw-r--r--src/jaegertracing/thrift/lib/rb/ext/compact_protocol.c637
-rw-r--r--src/jaegertracing/thrift/lib/rb/ext/compact_protocol.h20
-rw-r--r--src/jaegertracing/thrift/lib/rb/ext/constants.h99
-rw-r--r--src/jaegertracing/thrift/lib/rb/ext/extconf.rb34
-rw-r--r--src/jaegertracing/thrift/lib/rb/ext/macros.h41
-rw-r--r--src/jaegertracing/thrift/lib/rb/ext/memory_buffer.c134
-rw-r--r--src/jaegertracing/thrift/lib/rb/ext/memory_buffer.h20
-rw-r--r--src/jaegertracing/thrift/lib/rb/ext/protocol.c0
-rw-r--r--src/jaegertracing/thrift/lib/rb/ext/protocol.h0
-rw-r--r--src/jaegertracing/thrift/lib/rb/ext/strlcpy.c41
-rw-r--r--src/jaegertracing/thrift/lib/rb/ext/strlcpy.h34
-rw-r--r--src/jaegertracing/thrift/lib/rb/ext/struct.c711
-rw-r--r--src/jaegertracing/thrift/lib/rb/ext/struct.h25
-rw-r--r--src/jaegertracing/thrift/lib/rb/ext/thrift_native.c201
-rw-r--r--src/jaegertracing/thrift/lib/rb/lib/thrift.rb70
-rw-r--r--src/jaegertracing/thrift/lib/rb/lib/thrift/bytes.rb131
-rw-r--r--src/jaegertracing/thrift/lib/rb/lib/thrift/client.rb71
-rw-r--r--src/jaegertracing/thrift/lib/rb/lib/thrift/core_ext.rb23
-rw-r--r--src/jaegertracing/thrift/lib/rb/lib/thrift/core_ext/fixnum.rb29
-rw-r--r--src/jaegertracing/thrift/lib/rb/lib/thrift/exceptions.rb87
-rw-r--r--src/jaegertracing/thrift/lib/rb/lib/thrift/multiplexed_processor.rb76
-rw-r--r--src/jaegertracing/thrift/lib/rb/lib/thrift/processor.rb75
-rw-r--r--src/jaegertracing/thrift/lib/rb/lib/thrift/protocol/base_protocol.rb387
-rw-r--r--src/jaegertracing/thrift/lib/rb/lib/thrift/protocol/binary_protocol.rb244
-rw-r--r--src/jaegertracing/thrift/lib/rb/lib/thrift/protocol/binary_protocol_accelerated.rb47
-rw-r--r--src/jaegertracing/thrift/lib/rb/lib/thrift/protocol/compact_protocol.rb443
-rw-r--r--src/jaegertracing/thrift/lib/rb/lib/thrift/protocol/json_protocol.rb786
-rw-r--r--src/jaegertracing/thrift/lib/rb/lib/thrift/protocol/multiplexed_protocol.rb44
-rw-r--r--src/jaegertracing/thrift/lib/rb/lib/thrift/protocol/protocol_decorator.rb194
-rw-r--r--src/jaegertracing/thrift/lib/rb/lib/thrift/serializer/deserializer.rb33
-rw-r--r--src/jaegertracing/thrift/lib/rb/lib/thrift/serializer/serializer.rb34
-rw-r--r--src/jaegertracing/thrift/lib/rb/lib/thrift/server/base_server.rb37
-rw-r--r--src/jaegertracing/thrift/lib/rb/lib/thrift/server/mongrel_http_server.rb60
-rw-r--r--src/jaegertracing/thrift/lib/rb/lib/thrift/server/nonblocking_server.rb305
-rw-r--r--src/jaegertracing/thrift/lib/rb/lib/thrift/server/simple_server.rb47
-rw-r--r--src/jaegertracing/thrift/lib/rb/lib/thrift/server/thin_http_server.rb91
-rw-r--r--src/jaegertracing/thrift/lib/rb/lib/thrift/server/thread_pool_server.rb79
-rw-r--r--src/jaegertracing/thrift/lib/rb/lib/thrift/server/threaded_server.rb51
-rw-r--r--src/jaegertracing/thrift/lib/rb/lib/thrift/struct.rb237
-rw-r--r--src/jaegertracing/thrift/lib/rb/lib/thrift/struct_union.rb192
-rw-r--r--src/jaegertracing/thrift/lib/rb/lib/thrift/thrift_native.rb24
-rw-r--r--src/jaegertracing/thrift/lib/rb/lib/thrift/transport/base_server_transport.rb37
-rw-r--r--src/jaegertracing/thrift/lib/rb/lib/thrift/transport/base_transport.rb117
-rw-r--r--src/jaegertracing/thrift/lib/rb/lib/thrift/transport/buffered_transport.rb122
-rw-r--r--src/jaegertracing/thrift/lib/rb/lib/thrift/transport/framed_transport.rb125
-rw-r--r--src/jaegertracing/thrift/lib/rb/lib/thrift/transport/http_client_transport.rb61
-rw-r--r--src/jaegertracing/thrift/lib/rb/lib/thrift/transport/io_stream_transport.rb42
-rw-r--r--src/jaegertracing/thrift/lib/rb/lib/thrift/transport/memory_buffer_transport.rb129
-rw-r--r--src/jaegertracing/thrift/lib/rb/lib/thrift/transport/server_socket.rb68
-rw-r--r--src/jaegertracing/thrift/lib/rb/lib/thrift/transport/socket.rb143
-rw-r--r--src/jaegertracing/thrift/lib/rb/lib/thrift/transport/ssl_server_socket.rb41
-rw-r--r--src/jaegertracing/thrift/lib/rb/lib/thrift/transport/ssl_socket.rb51
-rw-r--r--src/jaegertracing/thrift/lib/rb/lib/thrift/transport/unix_server_socket.rb64
-rw-r--r--src/jaegertracing/thrift/lib/rb/lib/thrift/transport/unix_socket.rb44
-rw-r--r--src/jaegertracing/thrift/lib/rb/lib/thrift/types.rb101
-rw-r--r--src/jaegertracing/thrift/lib/rb/lib/thrift/union.rb176
-rw-r--r--src/jaegertracing/thrift/lib/rb/script/proto_benchmark.rb121
-rw-r--r--src/jaegertracing/thrift/lib/rb/script/read_struct.rb43
-rw-r--r--src/jaegertracing/thrift/lib/rb/script/write_struct.rb30
-rw-r--r--src/jaegertracing/thrift/lib/rb/spec/BaseService.thrift27
-rw-r--r--src/jaegertracing/thrift/lib/rb/spec/ExtendedService.thrift25
-rw-r--r--src/jaegertracing/thrift/lib/rb/spec/Referenced.thrift44
-rw-r--r--src/jaegertracing/thrift/lib/rb/spec/ThriftNamespacedSpec.thrift53
-rw-r--r--src/jaegertracing/thrift/lib/rb/spec/ThriftSpec.thrift183
-rw-r--r--src/jaegertracing/thrift/lib/rb/spec/base_protocol_spec.rb225
-rw-r--r--src/jaegertracing/thrift/lib/rb/spec/base_transport_spec.rb388
-rw-r--r--src/jaegertracing/thrift/lib/rb/spec/binary_protocol_accelerated_spec.rb46
-rw-r--r--src/jaegertracing/thrift/lib/rb/spec/binary_protocol_spec.rb74
-rw-r--r--src/jaegertracing/thrift/lib/rb/spec/binary_protocol_spec_shared.rb458
-rw-r--r--src/jaegertracing/thrift/lib/rb/spec/bytes_spec.rb160
-rw-r--r--src/jaegertracing/thrift/lib/rb/spec/client_spec.rb98
-rw-r--r--src/jaegertracing/thrift/lib/rb/spec/compact_protocol_spec.rb158
-rw-r--r--src/jaegertracing/thrift/lib/rb/spec/exception_spec.rb141
-rw-r--r--src/jaegertracing/thrift/lib/rb/spec/flat_spec.rb62
-rw-r--r--src/jaegertracing/thrift/lib/rb/spec/http_client_spec.rb139
-rw-r--r--src/jaegertracing/thrift/lib/rb/spec/json_protocol_spec.rb552
-rw-r--r--src/jaegertracing/thrift/lib/rb/spec/namespaced_spec.rb67
-rw-r--r--src/jaegertracing/thrift/lib/rb/spec/nonblocking_server_spec.rb263
-rw-r--r--src/jaegertracing/thrift/lib/rb/spec/processor_spec.rb80
-rw-r--r--src/jaegertracing/thrift/lib/rb/spec/serializer_spec.rb67
-rw-r--r--src/jaegertracing/thrift/lib/rb/spec/server_socket_spec.rb84
-rw-r--r--src/jaegertracing/thrift/lib/rb/spec/server_spec.rb187
-rw-r--r--src/jaegertracing/thrift/lib/rb/spec/socket_spec.rb68
-rw-r--r--src/jaegertracing/thrift/lib/rb/spec/socket_spec_shared.rb104
-rw-r--r--src/jaegertracing/thrift/lib/rb/spec/spec_helper.rb64
-rw-r--r--src/jaegertracing/thrift/lib/rb/spec/ssl_server_socket_spec.rb34
-rw-r--r--src/jaegertracing/thrift/lib/rb/spec/ssl_socket_spec.rb78
-rw-r--r--src/jaegertracing/thrift/lib/rb/spec/struct_nested_containers_spec.rb191
-rw-r--r--src/jaegertracing/thrift/lib/rb/spec/struct_spec.rb293
-rw-r--r--src/jaegertracing/thrift/lib/rb/spec/thin_http_server_spec.rb141
-rw-r--r--src/jaegertracing/thrift/lib/rb/spec/types_spec.rb118
-rw-r--r--src/jaegertracing/thrift/lib/rb/spec/union_spec.rb214
-rw-r--r--src/jaegertracing/thrift/lib/rb/spec/unix_socket_spec.rb116
-rw-r--r--src/jaegertracing/thrift/lib/rb/thrift.gemspec40
108 files changed, 13716 insertions, 0 deletions
diff --git a/src/jaegertracing/thrift/lib/rb/Gemfile b/src/jaegertracing/thrift/lib/rb/Gemfile
new file mode 100644
index 000000000..1c86af95d
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/Gemfile
@@ -0,0 +1,4 @@
+source "http://rubygems.org"
+
+gemspec
+
diff --git a/src/jaegertracing/thrift/lib/rb/Makefile.am b/src/jaegertracing/thrift/lib/rb/Makefile.am
new file mode 100755
index 000000000..1841065f5
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/Makefile.am
@@ -0,0 +1,55 @@
+#
+# 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.
+#
+
+DESTDIR ?= /
+
+if HAVE_BUNDLER
+
+all-local:
+ $(BUNDLER) install
+ $(BUNDLER) exec rake build_ext
+
+install-exec-hook:
+ $(BUNDLER) exec rake install
+
+clean-local:
+ $(BUNDLER) install
+ $(BUNDLER) exec rake clean
+ $(RM) -r spec/gen-rb/
+
+check-local: all
+ $(BUNDLER) install
+ $(BUNDLER) exec rake
+
+endif
+
+dist-hook:
+ $(RM) -r $(distdir)/spec/gen-rb/
+
+EXTRA_DIST = \
+ coding_standards.md \
+ Rakefile \
+ Gemfile \
+ thrift.gemspec \
+ lib \
+ ext \
+ benchmark \
+ script \
+ spec \
+ README.md
diff --git a/src/jaegertracing/thrift/lib/rb/README.md b/src/jaegertracing/thrift/lib/rb/README.md
new file mode 100644
index 000000000..b6e9a0792
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/README.md
@@ -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.
diff --git a/src/jaegertracing/thrift/lib/rb/Rakefile b/src/jaegertracing/thrift/lib/rb/Rakefile
new file mode 100644
index 000000000..5e5e5acaa
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/Rakefile
@@ -0,0 +1,120 @@
+#
+# 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'
+require 'rake'
+require 'rake/clean'
+require 'rspec/core/rake_task'
+
+THRIFT = '../../compiler/cpp/thrift'
+
+task :default => [:gem]
+task :spec => [:'gen-rb', :build_ext, :realspec]
+
+RSpec::Core::RakeTask.new(:realspec) do |t|
+ t.rspec_opts = ['--color', '--format d']
+end
+
+RSpec::Core::RakeTask.new(:'spec:rcov') do |t|
+ t.rspec_opts = ['--color', '--format d']
+ t.rcov = true
+ t.rcov_opts = ['--exclude', '^spec,/gems/']
+end
+
+desc 'Compile the .thrift files for the specs'
+task :'gen-rb' => [:'gen-rb:spec', :'gen-rb:namespaced_spec', :'gen-rb:flat_spec', :'gen-rb:benchmark', :'gen-rb:debug_proto']
+namespace :'gen-rb' do
+ task :'spec' do
+ dir = File.dirname(__FILE__) + '/spec'
+ sh THRIFT, '--gen', 'rb', '-o', dir, "#{dir}/ThriftSpec.thrift"
+ end
+
+ task :'namespaced_spec' do
+ dir = File.dirname(__FILE__) + '/spec'
+ sh THRIFT, '--gen', 'rb:namespaced', '--recurse', '-o', dir, "#{dir}/ThriftNamespacedSpec.thrift"
+ sh THRIFT, '--gen', 'rb:namespaced', '--recurse', '-o', dir, "#{dir}/BaseService.thrift"
+ sh THRIFT, '--gen', 'rb:namespaced', '--recurse', '-o', dir, "#{dir}/ExtendedService.thrift"
+ end
+
+ task :'flat_spec' do
+ dir = File.dirname(__FILE__) + '/spec'
+ mkdir_p("#{dir}/gen-rb/flat")
+ sh THRIFT, '--gen', 'rb', '--recurse', '-out', "#{dir}/gen-rb/flat", "#{dir}/ThriftNamespacedSpec.thrift"
+ end
+
+ task :'benchmark' do
+ dir = File.dirname(__FILE__) + '/benchmark'
+ sh THRIFT, '--gen', 'rb', '-o', dir, "#{dir}/Benchmark.thrift"
+ end
+
+ task :'debug_proto' do
+ sh "mkdir", "-p", "test/debug_proto"
+ sh THRIFT, '--gen', 'rb', "-o", "test/debug_proto", "../../test/DebugProtoTest.thrift"
+ end
+end
+
+desc "Build the native library"
+task :build_ext => :'gen-rb' do
+ next if defined?(RUBY_ENGINE) && RUBY_ENGINE =~ /jruby/
+ Dir::chdir(File::dirname('ext/extconf.rb')) do
+ unless sh "ruby #{File::basename('ext/extconf.rb')}"
+ $stderr.puts "Failed to run extconf"
+ break
+ end
+ unless sh "make"
+ $stderr.puts "make failed"
+ break
+ end
+ end
+end
+
+desc 'Run the compiler tests (requires full thrift checkout)'
+task :test do
+ # ensure this is a full thrift checkout and not a tarball of the ruby libs
+ cmd = 'head -1 ../../README.md 2>/dev/null | grep Thrift >/dev/null 2>/dev/null'
+ system(cmd) or fail "rake test requires a full thrift checkout"
+ sh 'make', '-C', File.dirname(__FILE__) + "/../../test/rb", "check"
+end
+
+desc 'Run benchmarking of NonblockingServer'
+task :benchmark do
+ ruby 'benchmark/benchmark.rb'
+end
+
+desc 'Builds the thrift gem'
+task :gem => [:spec, :build_ext] do
+ unless sh 'gem', 'build', 'thrift.gemspec'
+ $stderr.puts "Failed to build thrift gem"
+ break
+ end
+end
+
+desc 'Install the thrift gem'
+task :install => [:gem] do
+ unless sh 'gem', 'install', Dir.glob('thrift-*.gem').last
+ $stderr.puts "Failed to install thrift gem"
+ break
+ end
+end
+
+CLEAN.include [
+ '.bundle', 'benchmark/gen-rb', 'coverage', 'ext/*.{o,bundle,so,dll}', 'ext/mkmf.log',
+ 'ext/Makefile', 'ext/conftest.dSYM', 'Gemfile.lock', 'mkmf.log', 'pkg', 'spec/gen-rb',
+ 'test', 'thrift-*.gem'
+]
diff --git a/src/jaegertracing/thrift/lib/rb/benchmark/Benchmark.thrift b/src/jaegertracing/thrift/lib/rb/benchmark/Benchmark.thrift
new file mode 100644
index 000000000..eb5ae38e6
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/benchmark/Benchmark.thrift
@@ -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)
+}
diff --git a/src/jaegertracing/thrift/lib/rb/benchmark/benchmark.rb b/src/jaegertracing/thrift/lib/rb/benchmark/benchmark.rb
new file mode 100644
index 000000000..3dc67dd8c
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/benchmark/benchmark.rb
@@ -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
diff --git a/src/jaegertracing/thrift/lib/rb/benchmark/client.rb b/src/jaegertracing/thrift/lib/rb/benchmark/client.rb
new file mode 100644
index 000000000..703dc8f52
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/benchmark/client.rb
@@ -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
diff --git a/src/jaegertracing/thrift/lib/rb/benchmark/server.rb b/src/jaegertracing/thrift/lib/rb/benchmark/server.rb
new file mode 100644
index 000000000..74e13f414
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/benchmark/server.rb
@@ -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
diff --git a/src/jaegertracing/thrift/lib/rb/benchmark/thin_server.rb b/src/jaegertracing/thrift/lib/rb/benchmark/thin_server.rb
new file mode 100644
index 000000000..4de2eef38
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/benchmark/thin_server.rb
@@ -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
diff --git a/src/jaegertracing/thrift/lib/rb/coding_standards.md b/src/jaegertracing/thrift/lib/rb/coding_standards.md
new file mode 100644
index 000000000..fa0390bb5
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/coding_standards.md
@@ -0,0 +1 @@
+Please follow [General Coding Standards](/doc/coding_standards.md)
diff --git a/src/jaegertracing/thrift/lib/rb/ext/binary_protocol_accelerated.c b/src/jaegertracing/thrift/lib/rb/ext/binary_protocol_accelerated.c
new file mode 100644
index 000000000..65cbe5ffe
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/ext/binary_protocol_accelerated.c
@@ -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");
+}
diff --git a/src/jaegertracing/thrift/lib/rb/ext/binary_protocol_accelerated.h b/src/jaegertracing/thrift/lib/rb/ext/binary_protocol_accelerated.h
new file mode 100644
index 000000000..37baf4142
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/ext/binary_protocol_accelerated.h
@@ -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();
diff --git a/src/jaegertracing/thrift/lib/rb/ext/bytes.c b/src/jaegertracing/thrift/lib/rb/ext/bytes.c
new file mode 100644
index 000000000..8a6fac4ac
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/ext/bytes.c
@@ -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);
+}
diff --git a/src/jaegertracing/thrift/lib/rb/ext/bytes.h b/src/jaegertracing/thrift/lib/rb/ext/bytes.h
new file mode 100644
index 000000000..7108d83ff
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/ext/bytes.h
@@ -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);
diff --git a/src/jaegertracing/thrift/lib/rb/ext/compact_protocol.c b/src/jaegertracing/thrift/lib/rb/ext/compact_protocol.c
new file mode 100644
index 000000000..c0f46b958
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/ext/compact_protocol.c
@@ -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();
+}
diff --git a/src/jaegertracing/thrift/lib/rb/ext/compact_protocol.h b/src/jaegertracing/thrift/lib/rb/ext/compact_protocol.h
new file mode 100644
index 000000000..163915e94
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/ext/compact_protocol.h
@@ -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();
diff --git a/src/jaegertracing/thrift/lib/rb/ext/constants.h b/src/jaegertracing/thrift/lib/rb/ext/constants.h
new file mode 100644
index 000000000..e7aec4478
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/ext/constants.h
@@ -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;
diff --git a/src/jaegertracing/thrift/lib/rb/ext/extconf.rb b/src/jaegertracing/thrift/lib/rb/ext/extconf.rb
new file mode 100644
index 000000000..b35f60bf3
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/ext/extconf.rb
@@ -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
diff --git a/src/jaegertracing/thrift/lib/rb/ext/macros.h b/src/jaegertracing/thrift/lib/rb/ext/macros.h
new file mode 100644
index 000000000..265f6930d
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/ext/macros.h
@@ -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
diff --git a/src/jaegertracing/thrift/lib/rb/ext/memory_buffer.c b/src/jaegertracing/thrift/lib/rb/ext/memory_buffer.c
new file mode 100644
index 000000000..8b52c4a3e
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/ext/memory_buffer.c
@@ -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")));
+}
diff --git a/src/jaegertracing/thrift/lib/rb/ext/memory_buffer.h b/src/jaegertracing/thrift/lib/rb/ext/memory_buffer.h
new file mode 100644
index 000000000..b277fa6f6
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/ext/memory_buffer.h
@@ -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();
diff --git a/src/jaegertracing/thrift/lib/rb/ext/protocol.c b/src/jaegertracing/thrift/lib/rb/ext/protocol.c
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/ext/protocol.c
diff --git a/src/jaegertracing/thrift/lib/rb/ext/protocol.h b/src/jaegertracing/thrift/lib/rb/ext/protocol.h
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/ext/protocol.h
diff --git a/src/jaegertracing/thrift/lib/rb/ext/strlcpy.c b/src/jaegertracing/thrift/lib/rb/ext/strlcpy.c
new file mode 100644
index 000000000..6700ff2d4
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/ext/strlcpy.c
@@ -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
+
diff --git a/src/jaegertracing/thrift/lib/rb/ext/strlcpy.h b/src/jaegertracing/thrift/lib/rb/ext/strlcpy.h
new file mode 100644
index 000000000..f6fe0fe6b
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/ext/strlcpy.h
@@ -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
+
diff --git a/src/jaegertracing/thrift/lib/rb/ext/struct.c b/src/jaegertracing/thrift/lib/rb/ext/struct.c
new file mode 100644
index 000000000..e3aa855ed
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/ext/struct.c
@@ -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");
+}
diff --git a/src/jaegertracing/thrift/lib/rb/ext/struct.h b/src/jaegertracing/thrift/lib/rb/ext/struct.h
new file mode 100644
index 000000000..4748be5cb
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/ext/struct.h
@@ -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();
diff --git a/src/jaegertracing/thrift/lib/rb/ext/thrift_native.c b/src/jaegertracing/thrift/lib/rb/ext/thrift_native.c
new file mode 100644
index 000000000..3430b7c25
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/ext/thrift_native.c
@@ -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();
+}
diff --git a/src/jaegertracing/thrift/lib/rb/lib/thrift.rb b/src/jaegertracing/thrift/lib/rb/lib/thrift.rb
new file mode 100644
index 000000000..0f581229c
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/lib/thrift.rb
@@ -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'
diff --git a/src/jaegertracing/thrift/lib/rb/lib/thrift/bytes.rb b/src/jaegertracing/thrift/lib/rb/lib/thrift/bytes.rb
new file mode 100644
index 000000000..efd4f6440
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/lib/thrift/bytes.rb
@@ -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
diff --git a/src/jaegertracing/thrift/lib/rb/lib/thrift/client.rb b/src/jaegertracing/thrift/lib/rb/lib/thrift/client.rb
new file mode 100644
index 000000000..64ef05956
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/lib/thrift/client.rb
@@ -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
diff --git a/src/jaegertracing/thrift/lib/rb/lib/thrift/core_ext.rb b/src/jaegertracing/thrift/lib/rb/lib/thrift/core_ext.rb
new file mode 100644
index 000000000..f763cd534
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/lib/thrift/core_ext.rb
@@ -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
diff --git a/src/jaegertracing/thrift/lib/rb/lib/thrift/core_ext/fixnum.rb b/src/jaegertracing/thrift/lib/rb/lib/thrift/core_ext/fixnum.rb
new file mode 100644
index 000000000..b4fc90dd6
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/lib/thrift/core_ext/fixnum.rb
@@ -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 \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/rb/lib/thrift/exceptions.rb b/src/jaegertracing/thrift/lib/rb/lib/thrift/exceptions.rb
new file mode 100644
index 000000000..68cb9e03a
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/lib/thrift/exceptions.rb
@@ -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
diff --git a/src/jaegertracing/thrift/lib/rb/lib/thrift/multiplexed_processor.rb b/src/jaegertracing/thrift/lib/rb/lib/thrift/multiplexed_processor.rb
new file mode 100644
index 000000000..c734c04ba
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/lib/thrift/multiplexed_processor.rb
@@ -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
diff --git a/src/jaegertracing/thrift/lib/rb/lib/thrift/processor.rb b/src/jaegertracing/thrift/lib/rb/lib/thrift/processor.rb
new file mode 100644
index 000000000..ce21e120a
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/lib/thrift/processor.rb
@@ -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
diff --git a/src/jaegertracing/thrift/lib/rb/lib/thrift/protocol/base_protocol.rb b/src/jaegertracing/thrift/lib/rb/lib/thrift/protocol/base_protocol.rb
new file mode 100644
index 000000000..4d83a21dd
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/lib/thrift/protocol/base_protocol.rb
@@ -0,0 +1,387 @@
+#
+# 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::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
+ else
+ raise ProtocolException.new(ProtocolException::INVALID_DATA, 'Invalid data')
+ end
+ end
+
+ def to_s
+ "#{trans.to_s}"
+ end
+ end
+
+ class BaseProtocolFactory
+ def get_protocol(trans)
+ raise NotImplementedError
+ end
+
+ def to_s
+ "base"
+ end
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/lib/thrift/protocol/binary_protocol.rb b/src/jaegertracing/thrift/lib/rb/lib/thrift/protocol/binary_protocol.rb
new file mode 100644
index 000000000..d8279dbe6
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/lib/thrift/protocol/binary_protocol.rb
@@ -0,0 +1,244 @@
+#
+# 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
+
+ def to_s
+ "binary(#{super.to_s})"
+ end
+ end
+
+ class BinaryProtocolFactory < BaseProtocolFactory
+ def get_protocol(trans)
+ return Thrift::BinaryProtocol.new(trans)
+ end
+
+ def to_s
+ "binary"
+ end
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/lib/thrift/protocol/binary_protocol_accelerated.rb b/src/jaegertracing/thrift/lib/rb/lib/thrift/protocol/binary_protocol_accelerated.rb
new file mode 100644
index 000000000..09b02644d
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/lib/thrift/protocol/binary_protocol_accelerated.rb
@@ -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.
+#
+
+=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
+
+ def to_s
+ if (defined? BinaryProtocolAccelerated)
+ "binary-accel"
+ else
+ "binary"
+ end
+ end
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/lib/thrift/protocol/compact_protocol.rb b/src/jaegertracing/thrift/lib/rb/lib/thrift/protocol/compact_protocol.rb
new file mode 100644
index 000000000..1f9bd3060
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/lib/thrift/protocol/compact_protocol.rb
@@ -0,0 +1,443 @@
+#
+# 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
+
+ def to_s
+ "compact(#{super.to_s})"
+ 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
+
+ def to_s
+ "compact"
+ end
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/lib/thrift/protocol/json_protocol.rb b/src/jaegertracing/thrift/lib/rb/lib/thrift/protocol/json_protocol.rb
new file mode 100644
index 000000000..91e74e46b
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/lib/thrift/protocol/json_protocol.rb
@@ -0,0 +1,786 @@
+# 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
+
+ def to_s
+ "json(#{super.to_s})"
+ end
+ end
+
+ class JsonProtocolFactory < BaseProtocolFactory
+ def get_protocol(trans)
+ return Thrift::JsonProtocol.new(trans)
+ end
+
+ def to_s
+ "json"
+ end
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/lib/thrift/protocol/multiplexed_protocol.rb b/src/jaegertracing/thrift/lib/rb/lib/thrift/protocol/multiplexed_protocol.rb
new file mode 100644
index 000000000..b4428a734
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/lib/thrift/protocol/multiplexed_protocol.rb
@@ -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.
+
+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
+
+ def to_s
+ "multiplexed(#{@service_name=@protocol.to_s})"
+ end
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/lib/thrift/protocol/protocol_decorator.rb b/src/jaegertracing/thrift/lib/rb/lib/thrift/protocol/protocol_decorator.rb
new file mode 100644
index 000000000..b1e3c155d
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/lib/thrift/protocol/protocol_decorator.rb
@@ -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 \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/rb/lib/thrift/serializer/deserializer.rb b/src/jaegertracing/thrift/lib/rb/lib/thrift/serializer/deserializer.rb
new file mode 100644
index 000000000..d2ee325a5
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/lib/thrift/serializer/deserializer.rb
@@ -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 \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/rb/lib/thrift/serializer/serializer.rb b/src/jaegertracing/thrift/lib/rb/lib/thrift/serializer/serializer.rb
new file mode 100644
index 000000000..22316395d
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/lib/thrift/serializer/serializer.rb
@@ -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
+
diff --git a/src/jaegertracing/thrift/lib/rb/lib/thrift/server/base_server.rb b/src/jaegertracing/thrift/lib/rb/lib/thrift/server/base_server.rb
new file mode 100644
index 000000000..aa4d09ce4
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/lib/thrift/server/base_server.rb
@@ -0,0 +1,37 @@
+#
+# 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
+ raise NotImplementedError
+ end
+
+ def to_s
+ "server(#{@protocol_factory.to_s}(#{@transport_factory.to_s}(#{@server_transport.to_s})))"
+ end
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/lib/thrift/server/mongrel_http_server.rb b/src/jaegertracing/thrift/lib/rb/lib/thrift/server/mongrel_http_server.rb
new file mode 100644
index 000000000..de354c8f9
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/lib/thrift/server/mongrel_http_server.rb
@@ -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
diff --git a/src/jaegertracing/thrift/lib/rb/lib/thrift/server/nonblocking_server.rb b/src/jaegertracing/thrift/lib/rb/lib/thrift/server/nonblocking_server.rb
new file mode 100644
index 000000000..740f3417e
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/lib/thrift/server/nonblocking_server.rb
@@ -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
diff --git a/src/jaegertracing/thrift/lib/rb/lib/thrift/server/simple_server.rb b/src/jaegertracing/thrift/lib/rb/lib/thrift/server/simple_server.rb
new file mode 100644
index 000000000..905fe9bd8
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/lib/thrift/server/simple_server.rb
@@ -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.
+#
+
+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
+
+ def to_s
+ "simple(#{super.to_s})"
+ end
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/lib/thrift/server/thin_http_server.rb b/src/jaegertracing/thrift/lib/rb/lib/thrift/server/thin_http_server.rb
new file mode 100644
index 000000000..4a81c6d17
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/lib/thrift/server/thin_http_server.rb
@@ -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
diff --git a/src/jaegertracing/thrift/lib/rb/lib/thrift/server/thread_pool_server.rb b/src/jaegertracing/thrift/lib/rb/lib/thrift/server/thread_pool_server.rb
new file mode 100644
index 000000000..bb754ad2b
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/lib/thrift/server/thread_pool_server.rb
@@ -0,0 +1,79 @@
+#
+# 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
+
+ def to_s
+ "threadpool(#{super.to_s})"
+ end
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/lib/thrift/server/threaded_server.rb b/src/jaegertracing/thrift/lib/rb/lib/thrift/server/threaded_server.rb
new file mode 100644
index 000000000..88ee1833f
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/lib/thrift/server/threaded_server.rb
@@ -0,0 +1,51 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+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
+
+ def to_s
+ "threaded(#{super.to_s})"
+ end
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/lib/thrift/struct.rb b/src/jaegertracing/thrift/lib/rb/lib/thrift/struct.rb
new file mode 100644
index 000000000..df9d6561c
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/lib/thrift/struct.rb
@@ -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
diff --git a/src/jaegertracing/thrift/lib/rb/lib/thrift/struct_union.rb b/src/jaegertracing/thrift/lib/rb/lib/thrift/struct_union.rb
new file mode 100644
index 000000000..e21b39ebf
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/lib/thrift/struct_union.rb
@@ -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 \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/rb/lib/thrift/thrift_native.rb b/src/jaegertracing/thrift/lib/rb/lib/thrift/thrift_native.rb
new file mode 100644
index 000000000..4d8df61ff
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/lib/thrift/thrift_native.rb
@@ -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 \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/rb/lib/thrift/transport/base_server_transport.rb b/src/jaegertracing/thrift/lib/rb/lib/thrift/transport/base_server_transport.rb
new file mode 100644
index 000000000..0105463f8
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/lib/thrift/transport/base_server_transport.rb
@@ -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
diff --git a/src/jaegertracing/thrift/lib/rb/lib/thrift/transport/base_transport.rb b/src/jaegertracing/thrift/lib/rb/lib/thrift/transport/base_transport.rb
new file mode 100644
index 000000000..97e59352a
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/lib/thrift/transport/base_transport.rb
@@ -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 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
+
+ def to_s
+ "base"
+ end
+ end
+
+ class BaseTransportFactory
+ def get_transport(trans)
+ return trans
+ end
+
+ def to_s
+ "base"
+ end
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/lib/thrift/transport/buffered_transport.rb b/src/jaegertracing/thrift/lib/rb/lib/thrift/transport/buffered_transport.rb
new file mode 100644
index 000000000..4fe9c41a5
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/lib/thrift/transport/buffered_transport.rb
@@ -0,0 +1,122 @@
+# 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
+
+ def to_s
+ "buffered(#{@transport.to_s})"
+ end
+ end
+
+ class BufferedTransportFactory < BaseTransportFactory
+ def get_transport(transport)
+ return BufferedTransport.new(transport)
+ end
+
+ def to_s
+ "buffered"
+ end
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/lib/thrift/transport/framed_transport.rb b/src/jaegertracing/thrift/lib/rb/lib/thrift/transport/framed_transport.rb
new file mode 100644
index 000000000..953177821
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/lib/thrift/transport/framed_transport.rb
@@ -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 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
+
+ def to_s
+ "framed(#{@transport.to_s})"
+ 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
+
+ def to_s
+ "framed"
+ end
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/lib/thrift/transport/http_client_transport.rb b/src/jaegertracing/thrift/lib/rb/lib/thrift/transport/http_client_transport.rb
new file mode 100644
index 000000000..5c1dd5c8a
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/lib/thrift/transport/http_client_transport.rb
@@ -0,0 +1,61 @@
+# 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
+
+ def to_s
+ "@{self.url}"
+ end
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/lib/thrift/transport/io_stream_transport.rb b/src/jaegertracing/thrift/lib/rb/lib/thrift/transport/io_stream_transport.rb
new file mode 100644
index 000000000..ccec68f25
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/lib/thrift/transport/io_stream_transport.rb
@@ -0,0 +1,42 @@
+# 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
+ def to_s
+ "iostream(input=#{@input.to_s},output=#{@output.to_s})"
+ end
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/lib/thrift/transport/memory_buffer_transport.rb b/src/jaegertracing/thrift/lib/rb/lib/thrift/transport/memory_buffer_transport.rb
new file mode 100644
index 000000000..469ea7396
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/lib/thrift/transport/memory_buffer_transport.rb
@@ -0,0 +1,129 @@
+# 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
+
+ def to_s
+ "memory"
+ end
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/lib/thrift/transport/server_socket.rb b/src/jaegertracing/thrift/lib/rb/lib/thrift/transport/server_socket.rb
new file mode 100644
index 000000000..50002324e
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/lib/thrift/transport/server_socket.rb
@@ -0,0 +1,68 @@
+# 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
+
+ def to_s
+ "socket(#{@host}:#{@port})"
+ end
+
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/lib/thrift/transport/socket.rb b/src/jaegertracing/thrift/lib/rb/lib/thrift/transport/socket.rb
new file mode 100644
index 000000000..f5e6f3b85
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/lib/thrift/transport/socket.rb
@@ -0,0 +1,143 @@
+# 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
+
+ alias to_io handle
+
+ def to_s
+ "socket(#{@host}:#{@port})"
+ end
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/lib/thrift/transport/ssl_server_socket.rb b/src/jaegertracing/thrift/lib/rb/lib/thrift/transport/ssl_server_socket.rb
new file mode 100644
index 000000000..3abd5ec3d
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/lib/thrift/transport/ssl_server_socket.rb
@@ -0,0 +1,41 @@
+# 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
+
+ def to_s
+ "ssl(#{super.to_s})"
+ end
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/lib/thrift/transport/ssl_socket.rb b/src/jaegertracing/thrift/lib/rb/lib/thrift/transport/ssl_socket.rb
new file mode 100644
index 000000000..7ab96ab45
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/lib/thrift/transport/ssl_socket.rb
@@ -0,0 +1,51 @@
+# 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
+
+ def to_s
+ "ssl(#{super.to_s})"
+ end
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/lib/thrift/transport/unix_server_socket.rb b/src/jaegertracing/thrift/lib/rb/lib/thrift/transport/unix_server_socket.rb
new file mode 100644
index 000000000..057d122e7
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/lib/thrift/transport/unix_server_socket.rb
@@ -0,0 +1,64 @@
+# 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
+
+ def to_s
+ "domain(#{@path})"
+ end
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/lib/thrift/transport/unix_socket.rb b/src/jaegertracing/thrift/lib/rb/lib/thrift/transport/unix_socket.rb
new file mode 100644
index 000000000..5dffd59f2
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/lib/thrift/transport/unix_socket.rb
@@ -0,0 +1,44 @@
+# 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
+
+ def to_s
+ "domain(#{@path})"
+ end
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/lib/thrift/types.rb b/src/jaegertracing/thrift/lib/rb/lib/thrift/types.rb
new file mode 100644
index 000000000..cac52691a
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/lib/thrift/types.rb
@@ -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?
diff --git a/src/jaegertracing/thrift/lib/rb/lib/thrift/union.rb b/src/jaegertracing/thrift/lib/rb/lib/thrift/union.rb
new file mode 100644
index 000000000..490c55c40
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/lib/thrift/union.rb
@@ -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
diff --git a/src/jaegertracing/thrift/lib/rb/script/proto_benchmark.rb b/src/jaegertracing/thrift/lib/rb/script/proto_benchmark.rb
new file mode 100644
index 000000000..bb49e2e42
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/script/proto_benchmark.rb
@@ -0,0 +1,121 @@
+#
+# 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 File.dirname(__FILE__) + "/../spec/spec_helper.rb"
+
+require "benchmark"
+# require "ruby-prof"
+
+obj = Fixtures::COMPACT_PROTOCOL_TEST_STRUCT
+
+HOW_MANY = 1_000
+
+binser = Thrift::Serializer.new
+bin_data = binser.serialize(obj)
+bindeser = Thrift::Deserializer.new
+accel_bin_ser = Thrift::Serializer.new(Thrift::BinaryProtocolAcceleratedFactory.new)
+accel_bin_deser = Thrift::Deserializer.new(Thrift::BinaryProtocolAcceleratedFactory.new)
+
+compact_ser = Thrift::Serializer.new(Thrift::CompactProtocolFactory.new)
+compact_data = compact_ser.serialize(obj)
+compact_deser = Thrift::Deserializer.new(Thrift::CompactProtocolFactory.new)
+
+Benchmark.bm(60) do |reporter|
+ reporter.report("binary protocol, write") do
+ HOW_MANY.times do
+ binser.serialize(obj)
+ end
+ end
+
+ reporter.report("accelerated binary protocol, write") do
+ HOW_MANY.times do
+ accel_bin_ser.serialize(obj)
+ end
+ end
+
+ reporter.report("compact protocol, write") do
+ # RubyProf.start
+ HOW_MANY.times do
+ compact_ser.serialize(obj)
+ end
+ # result = RubyProf.stop
+ # printer = RubyProf::GraphHtmlPrinter.new(result)
+ # file = File.open("profile.html", "w+")
+ # printer.print(file, 0)
+ # file.close
+ end
+
+ reporter.report("binary protocol, read") do
+ HOW_MANY.times do
+ bindeser.deserialize(obj, bin_data)
+ end
+ end
+
+ reporter.report("accelerated binary protocol, read") do
+ HOW_MANY.times do
+ accel_bin_deser.deserialize(obj, bin_data)
+ end
+ end
+
+ reporter.report("compact protocol, read") do
+ HOW_MANY.times do
+ compact_deser.deserialize(obj, compact_data)
+ end
+ end
+
+
+ # f = File.new("/tmp/testfile", "w")
+ # proto = Thrift::BinaryProtocolAccelerated.new(Thrift::IOStreamTransport.new(Thrift::MemoryBufferTransport.new, f))
+ # reporter.report("accelerated binary protocol, write (to disk)") do
+ # HOW_MANY.times do
+ # obj.write(proto)
+ # end
+ # f.flush
+ # end
+ # f.close
+ #
+ # f = File.new("/tmp/testfile", "r")
+ # proto = Thrift::BinaryProtocolAccelerated.new(Thrift::IOStreamTransport.new(f, Thrift::MemoryBufferTransport.new))
+ # reporter.report("accelerated binary protocol, read (from disk)") do
+ # HOW_MANY.times do
+ # obj.read(proto)
+ # end
+ # end
+ # f.close
+ #
+ # f = File.new("/tmp/testfile", "w")
+ # reporter.report("compact protocol, write (to disk)") do
+ # proto = Thrift::CompactProtocol.new(Thrift::IOStreamTransport.new(Thrift::MemoryBufferTransport.new, f))
+ # HOW_MANY.times do
+ # obj.write(proto)
+ # end
+ # f.flush
+ # end
+ # f.close
+ #
+ # f = File.new("/tmp/testfile", "r")
+ # reporter.report("compact protocol, read (from disk)") do
+ # proto = Thrift::CompactProtocol.new(Thrift::IOStreamTransport.new(f, Thrift::MemoryBufferTransport.new))
+ # HOW_MANY.times do
+ # obj.read(proto)
+ # end
+ # end
+ # f.close
+
+end
diff --git a/src/jaegertracing/thrift/lib/rb/script/read_struct.rb b/src/jaegertracing/thrift/lib/rb/script/read_struct.rb
new file mode 100644
index 000000000..831fcec90
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/script/read_struct.rb
@@ -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.
+#
+
+require "spec/spec_helper"
+
+path, factory_class = ARGV
+
+factory = eval(factory_class).new
+
+deser = Thrift::Deserializer.new(factory)
+
+cpts = CompactProtoTestStruct.new
+CompactProtoTestStruct.constants.each do |const|
+ cpts.instance_variable_set("@#{const}", nil)
+end
+
+data = File.read(path)
+
+deser.deserialize(cpts, data)
+
+if cpts == Fixtures::COMPACT_PROTOCOL_TEST_STRUCT
+ puts "Object verified successfully!"
+else
+ puts "Object failed verification! Expected #{Fixtures::COMPACT_PROTOCOL_TEST_STRUCT.inspect} but got #{cpts.inspect}"
+
+ puts cpts.differences(Fixtures::COMPACT_PROTOCOL_TEST_STRUCT)
+end
diff --git a/src/jaegertracing/thrift/lib/rb/script/write_struct.rb b/src/jaegertracing/thrift/lib/rb/script/write_struct.rb
new file mode 100644
index 000000000..da1421975
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/script/write_struct.rb
@@ -0,0 +1,30 @@
+#
+# 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/spec_helper"
+
+path, factory_class = ARGV
+
+factory = eval(factory_class).new
+
+ser = Thrift::Serializer.new(factory)
+
+File.open(path, "w") do |file|
+ file.write(ser.serialize(Fixtures::COMPACT_PROTOCOL_TEST_STRUCT))
+end \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/rb/spec/BaseService.thrift b/src/jaegertracing/thrift/lib/rb/spec/BaseService.thrift
new file mode 100644
index 000000000..5c7d32a6c
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/spec/BaseService.thrift
@@ -0,0 +1,27 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+namespace rb Base
+
+struct Hello {
+ 1: string greeting = "hello world"
+}
+
+service BaseService {
+ Hello greeting(1:bool english)
+}
diff --git a/src/jaegertracing/thrift/lib/rb/spec/ExtendedService.thrift b/src/jaegertracing/thrift/lib/rb/spec/ExtendedService.thrift
new file mode 100644
index 000000000..1a6b705aa
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/spec/ExtendedService.thrift
@@ -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()
+}
diff --git a/src/jaegertracing/thrift/lib/rb/spec/Referenced.thrift b/src/jaegertracing/thrift/lib/rb/spec/Referenced.thrift
new file mode 100644
index 000000000..98f183fe0
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/spec/Referenced.thrift
@@ -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
+}
diff --git a/src/jaegertracing/thrift/lib/rb/spec/ThriftNamespacedSpec.thrift b/src/jaegertracing/thrift/lib/rb/spec/ThriftNamespacedSpec.thrift
new file mode 100644
index 000000000..02f28895a
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/spec/ThriftNamespacedSpec.thrift
@@ -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)
+}
diff --git a/src/jaegertracing/thrift/lib/rb/spec/ThriftSpec.thrift b/src/jaegertracing/thrift/lib/rb/spec/ThriftSpec.thrift
new file mode 100644
index 000000000..b42481b32
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/spec/ThriftSpec.thrift
@@ -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
+}
diff --git a/src/jaegertracing/thrift/lib/rb/spec/base_protocol_spec.rb b/src/jaegertracing/thrift/lib/rb/spec/base_protocol_spec.rb
new file mode 100644
index 000000000..cfa7573d8
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/spec/base_protocol_spec.rb
@@ -0,0 +1,225 @@
+#
+# 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 = double("MockTransport")
+ @prot = Thrift::BaseProtocol.new(@trans)
+ end
+
+ describe Thrift::BaseProtocol do
+ # most of the methods are stubs, so we can ignore them
+
+ it "should provide a reasonable to_s" do
+ expect(@trans).to receive(:to_s).once.and_return("trans")
+ expect(@prot.to_s).to eq("trans")
+ end
+
+ it "should make trans accessible" do
+ expect(@prot.trans).to eql(@trans)
+ end
+
+ it 'should write out a field nicely (deprecated write_field signature)' do
+ expect(@prot).to receive(:write_field_begin).with('field', 'type', 'fid').ordered
+ expect(@prot).to receive(:write_type).with({:name => 'field', :type => 'type'}, 'value').ordered
+ expect(@prot).to receive(:write_field_end).ordered
+ @prot.write_field('field', 'type', 'fid', 'value')
+ end
+
+ it 'should write out a field nicely' do
+ expect(@prot).to receive(:write_field_begin).with('field', 'type', 'fid').ordered
+ expect(@prot).to receive(:write_type).with({:name => 'field', :type => 'type', :binary => false}, 'value').ordered
+ expect(@prot).to 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
+ expect(@prot).to receive(:write_bool).with('bool').ordered
+ expect(@prot).to receive(:write_byte).with('byte').ordered
+ expect(@prot).to receive(:write_double).with('double').ordered
+ expect(@prot).to receive(:write_i16).with('i16').ordered
+ expect(@prot).to receive(:write_i32).with('i32').ordered
+ expect(@prot).to receive(:write_i64).with('i64').ordered
+ expect(@prot).to receive(:write_string).with('string').ordered
+ struct = double('Struct')
+ expect(struct).to 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
+ expect(@prot).to receive(:write_bool).with('bool').ordered
+ expect(@prot).to receive(:write_byte).with('byte').ordered
+ expect(@prot).to receive(:write_double).with('double').ordered
+ expect(@prot).to receive(:write_i16).with('i16').ordered
+ expect(@prot).to receive(:write_i32).with('i32').ordered
+ expect(@prot).to receive(:write_i64).with('i64').ordered
+ expect(@prot).to receive(:write_string).with('string').ordered
+ expect(@prot).to receive(:write_binary).with('binary').ordered
+ struct = double('Struct')
+ expect(struct).to 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
+ expect(@prot).to receive(:read_bool).ordered
+ expect(@prot).to receive(:read_byte).ordered
+ expect(@prot).to receive(:read_i16).ordered
+ expect(@prot).to receive(:read_i32).ordered
+ expect(@prot).to receive(:read_i64).ordered
+ expect(@prot).to receive(:read_double).ordered
+ expect(@prot).to 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
+ expect(@prot).to receive(:read_bool).ordered
+ expect(@prot).to receive(:read_byte).ordered
+ expect(@prot).to receive(:read_i16).ordered
+ expect(@prot).to receive(:read_i32).ordered
+ expect(@prot).to receive(:read_i64).ordered
+ expect(@prot).to receive(:read_double).ordered
+ expect(@prot).to receive(:read_string).ordered
+ expect(@prot).to 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
+ expect(@prot).to receive(:read_bool).ordered
+ expect(@prot).to receive(:read_byte).ordered
+ expect(@prot).to receive(:read_i16).ordered
+ expect(@prot).to receive(:read_i32).ordered
+ expect(@prot).to receive(:read_i64).ordered
+ expect(@prot).to receive(:read_double).ordered
+ expect(@prot).to 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)
+ end
+
+ it "should skip structs" do
+ real_skip = @prot.method(:skip)
+ expect(@prot).to receive(:read_struct_begin).ordered
+ expect(@prot).to 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]
+ )
+ expect(@prot).to receive(:read_field_end).exactly(3).times
+ expect(@prot).to receive(:read_string).exactly(3).times
+ expect(@prot).to receive(:read_i32).ordered
+ expect(@prot).to receive(:read_map_begin).ordered.and_return([Thrift::Types::STRING, Thrift::Types::STRING, 1])
+ # @prot.should_receive(:read_string).exactly(2).times
+ expect(@prot).to receive(:read_map_end).ordered
+ expect(@prot).to receive(:read_struct_end).ordered
+ real_skip.call(Thrift::Types::STRUCT)
+ end
+
+ it "should skip maps" do
+ real_skip = @prot.method(:skip)
+ expect(@prot).to receive(:read_map_begin).ordered.and_return([Thrift::Types::STRING, Thrift::Types::STRUCT, 1])
+ expect(@prot).to receive(:read_string).ordered
+ expect(@prot).to receive(:read_struct_begin).ordered.and_return(["some_struct"])
+ expect(@prot).to receive(:read_field_begin).ordered.and_return([nil, Thrift::Types::STOP, nil]);
+ expect(@prot).to receive(:read_struct_end).ordered
+ expect(@prot).to receive(:read_map_end).ordered
+ real_skip.call(Thrift::Types::MAP)
+ end
+
+ it "should skip sets" do
+ real_skip = @prot.method(:skip)
+ expect(@prot).to receive(:read_set_begin).ordered.and_return([Thrift::Types::I64, 9])
+ expect(@prot).to receive(:read_i64).ordered.exactly(9).times
+ expect(@prot).to receive(:read_set_end)
+ real_skip.call(Thrift::Types::SET)
+ end
+
+ it "should skip lists" do
+ real_skip = @prot.method(:skip)
+ expect(@prot).to receive(:read_list_begin).ordered.and_return([Thrift::Types::DOUBLE, 11])
+ expect(@prot).to receive(:read_double).ordered.exactly(11).times
+ expect(@prot).to 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
+ expect {Thrift::BaseProtocolFactory.new.get_protocol(double("MockTransport"))}.to raise_error(NotImplementedError)
+ end
+
+ it "should provide a reasonable to_s" do
+ expect(Thrift::BaseProtocolFactory.new.to_s).to eq("base")
+ end
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/spec/base_transport_spec.rb b/src/jaegertracing/thrift/lib/rb/spec/base_transport_spec.rb
new file mode 100644
index 000000000..d2f60aaea
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/spec/base_transport_spec.rb
@@ -0,0 +1,388 @@
+#
+# 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")
+ expect(exc.type).to eq(Thrift::TransportException::ALREADY_OPEN)
+ expect(exc.message).to eq("msg")
+ end
+ end
+
+ describe Thrift::BaseTransport do
+ it "should read the specified size" do
+ transport = Thrift::BaseTransport.new
+ expect(transport).to receive(:read).with(40).ordered.and_return("10 letters")
+ expect(transport).to receive(:read).with(30).ordered.and_return("fifteen letters")
+ expect(transport).to receive(:read).with(15).ordered.and_return("more characters")
+ expect(transport.read_all(40)).to eq("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|
+ expect(Thrift::BaseTransport.method_defined?(sym)).to be_truthy
+ end
+ end
+
+ it "should alias << to write" do
+ expect(Thrift::BaseTransport.instance_method(:<<)).to eq(Thrift::BaseTransport.instance_method(:write))
+ end
+
+ it "should provide a reasonable to_s" do
+ expect(Thrift::BaseTransport.new.to_s).to eq("base")
+ end
+ end
+
+ describe Thrift::BaseServerTransport do
+ it "should stub out its methods" do
+ [:listen, :accept, :close].each do |sym|
+ expect(Thrift::BaseServerTransport.method_defined?(sym)).to be_truthy
+ end
+ end
+ end
+
+ describe Thrift::BaseTransportFactory do
+ it "should return the transport it's given" do
+ transport = double("Transport")
+ expect(Thrift::BaseTransportFactory.new.get_transport(transport)).to eql(transport)
+ end
+
+ it "should provide a reasonable to_s" do
+ expect(Thrift::BaseTransportFactory.new.to_s).to eq("base")
+ end
+ end
+
+ describe Thrift::BufferedTransport do
+ it "should provide a to_s that describes the encapsulation" do
+ trans = double("Transport")
+ expect(trans).to receive(:to_s).and_return("mock")
+ expect(Thrift::BufferedTransport.new(trans).to_s).to eq("buffered(mock)")
+ end
+
+ it "should pass through everything but write/flush/read" do
+ trans = double("Transport")
+ expect(trans).to receive(:open?).ordered.and_return("+ open?")
+ expect(trans).to receive(:open).ordered.and_return("+ open")
+ expect(trans).to receive(:flush).ordered # from the close
+ expect(trans).to receive(:close).ordered.and_return("+ close")
+ btrans = Thrift::BufferedTransport.new(trans)
+ expect(btrans.open?).to eq("+ open?")
+ expect(btrans.open).to eq("+ open")
+ expect(btrans.close).to eq("+ close")
+ end
+
+ it "should buffer reads in chunks of #{Thrift::BufferedTransport::DEFAULT_BUFFER}" do
+ trans = double("Transport")
+ expect(trans).to receive(:read).with(Thrift::BufferedTransport::DEFAULT_BUFFER).and_return("lorum ipsum dolor emet")
+ btrans = Thrift::BufferedTransport.new(trans)
+ expect(btrans.read(6)).to eq("lorum ")
+ expect(btrans.read(6)).to eq("ipsum ")
+ expect(btrans.read(6)).to eq("dolor ")
+ expect(btrans.read(6)).to eq("emet")
+ end
+
+ it "should buffer writes and send them on flush" do
+ trans = double("Transport")
+ btrans = Thrift::BufferedTransport.new(trans)
+ btrans.write("one/")
+ btrans.write("two/")
+ btrans.write("three/")
+ expect(trans).to receive(:write).with("one/two/three/").ordered
+ expect(trans).to receive(:flush).ordered
+ btrans.flush
+ end
+
+ it "should only send buffered data once" do
+ trans = double("Transport")
+ btrans = Thrift::BufferedTransport.new(trans)
+ btrans.write("one/")
+ btrans.write("two/")
+ btrans.write("three/")
+ expect(trans).to receive(:write).with("one/two/three/")
+ allow(trans).to receive(:flush)
+ btrans.flush
+ # Nothing to flush with no data
+ btrans.flush
+ end
+
+ it "should flush on close" do
+ trans = double("Transport")
+ expect(trans).to receive(:close)
+ btrans = Thrift::BufferedTransport.new(trans)
+ expect(btrans).to receive(:flush)
+ btrans.close
+ end
+
+ it "should not write to socket if there's no data" do
+ trans = double("Transport")
+ expect(trans).to 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 = double("Transport")
+ btrans = double("BufferedTransport")
+ expect(Thrift::BufferedTransport).to receive(:new).with(trans).and_return(btrans)
+ expect(Thrift::BufferedTransportFactory.new.get_transport(trans)).to eq(btrans)
+ end
+
+ it "should provide a reasonable to_s" do
+ expect(Thrift::BufferedTransportFactory.new.to_s).to eq("buffered")
+ end
+ end
+
+ describe Thrift::FramedTransport do
+ before(:each) do
+ @trans = double("Transport")
+ end
+
+ it "should provide a to_s that describes the encapsulation" do
+ trans = double("Transport")
+ expect(trans).to receive(:to_s).and_return("mock")
+ expect(Thrift::FramedTransport.new(trans).to_s).to eq("framed(mock)")
+ end
+
+ it "should pass through open?/open/close" do
+ ftrans = Thrift::FramedTransport.new(@trans)
+ expect(@trans).to receive(:open?).ordered.and_return("+ open?")
+ expect(@trans).to receive(:open).ordered.and_return("+ open")
+ expect(@trans).to receive(:close).ordered.and_return("+ close")
+ expect(ftrans.open?).to eq("+ open?")
+ expect(ftrans.open).to eq("+ open")
+ expect(ftrans.close).to eq("+ close")
+ end
+
+ it "should pass through read when read is turned off" do
+ ftrans = Thrift::FramedTransport.new(@trans, false, true)
+ expect(@trans).to receive(:read).with(17).ordered.and_return("+ read")
+ expect(ftrans.read(17)).to eq("+ read")
+ end
+
+ it "should pass through write/flush when write is turned off" do
+ ftrans = Thrift::FramedTransport.new(@trans, true, false)
+ expect(@trans).to receive(:write).with("foo").ordered.and_return("+ write")
+ expect(@trans).to receive(:flush).ordered.and_return("+ flush")
+ expect(ftrans.write("foo")).to eq("+ write")
+ expect(ftrans.flush).to eq("+ flush")
+ end
+
+ it "should return a full frame if asked for >= the frame's length" do
+ frame = "this is a frame"
+ expect(@trans).to receive(:read_all).with(4).and_return("\000\000\000\017")
+ expect(@trans).to receive(:read_all).with(frame.length).and_return(frame)
+ expect(Thrift::FramedTransport.new(@trans).read(frame.length + 10)).to eq(frame)
+ end
+
+ it "should return slices of the frame when asked for < the frame's length" do
+ frame = "this is a frame"
+ expect(@trans).to receive(:read_all).with(4).and_return("\000\000\000\017")
+ expect(@trans).to receive(:read_all).with(frame.length).and_return(frame)
+ ftrans = Thrift::FramedTransport.new(@trans)
+ expect(ftrans.read(4)).to eq("this")
+ expect(ftrans.read(4)).to eq(" is ")
+ expect(ftrans.read(16)).to eq("a frame")
+ end
+
+ it "should return nothing if asked for <= 0" do
+ expect(Thrift::FramedTransport.new(@trans).read(-2)).to eq("")
+ end
+
+ it "should pull a new frame when the first is exhausted" do
+ frame = "this is a frame"
+ frame2 = "yet another frame"
+ expect(@trans).to receive(:read_all).with(4).and_return("\000\000\000\017", "\000\000\000\021")
+ expect(@trans).to receive(:read_all).with(frame.length).and_return(frame)
+ expect(@trans).to receive(:read_all).with(frame2.length).and_return(frame2)
+ ftrans = Thrift::FramedTransport.new(@trans)
+ expect(ftrans.read(4)).to eq("this")
+ expect(ftrans.read(8)).to eq(" is a fr")
+ expect(ftrans.read(6)).to eq("ame")
+ expect(ftrans.read(4)).to eq("yet ")
+ expect(ftrans.read(16)).to eq("another frame")
+ end
+
+ it "should buffer writes" do
+ ftrans = Thrift::FramedTransport.new(@trans)
+ expect(@trans).not_to 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)
+ allow(@trans).to receive(:flush)
+ expect(@trans).to 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)
+ expect(@trans).to receive(:write).with("\000\000\000\035one/two/three/this is a frame").ordered
+ expect(@trans).to 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)
+ expect(@trans).to receive(:write).with("\000\000\000\007foo/bar")
+ allow(@trans).to receive(:flush)
+ ftrans.write("foo")
+ ftrans.write("/bar")
+ ftrans.flush
+ expect(@trans).to 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 = double("Transport")
+ expect(Thrift::FramedTransport).to receive(:new).with(trans)
+ Thrift::FramedTransportFactory.new.get_transport(trans)
+ end
+
+ it "should provide a reasonable to_s" do
+ expect(Thrift::FramedTransportFactory.new.to_s).to eq("framed")
+ end
+ end
+
+ describe Thrift::MemoryBufferTransport do
+ before(:each) do
+ @buffer = Thrift::MemoryBufferTransport.new
+ end
+
+ it "should provide a reasonable to_s" do
+ expect(@buffer.to_s).to eq("memory")
+ end
+
+ it "should accept a buffer on input and use it directly" do
+ s = "this is a test"
+ @buffer = Thrift::MemoryBufferTransport.new(s)
+ expect(@buffer.read(4)).to eq("this")
+ s.slice!(-4..-1)
+ expect(@buffer.read(@buffer.available)).to eq(" is a ")
+ end
+
+ it "should always remain open" do
+ expect(@buffer).to be_open
+ @buffer.close
+ expect(@buffer).to be_open
+ end
+
+ it "should respond to peek and available" do
+ @buffer.write "some data"
+ expect(@buffer.peek).to be_truthy
+ expect(@buffer.available).to eq(9)
+ @buffer.read(4)
+ expect(@buffer.peek).to be_truthy
+ expect(@buffer.available).to eq(5)
+ @buffer.read(5)
+ expect(@buffer.peek).to be_falsey
+ expect(@buffer.available).to eq(0)
+ end
+
+ it "should be able to reset the buffer" do
+ @buffer.write "test data"
+ @buffer.reset_buffer("foobar")
+ expect(@buffer.available).to eq(6)
+ expect(@buffer.read(@buffer.available)).to eq("foobar")
+ @buffer.reset_buffer
+ expect(@buffer.available).to eq(0)
+ end
+
+ it "should copy the given string when resetting the buffer" do
+ s = "this is a test"
+ @buffer.reset_buffer(s)
+ expect(@buffer.available).to eq(14)
+ @buffer.read(10)
+ expect(@buffer.available).to eq(4)
+ expect(s).to eq("this is a test")
+ end
+
+ it "should return from read what was given in write" do
+ @buffer.write "test data"
+ expect(@buffer.read(4)).to eq("test")
+ expect(@buffer.read(@buffer.available)).to eq(" data")
+ @buffer.write "foo"
+ @buffer.write " bar"
+ expect(@buffer.read(@buffer.available)).to eq("foo bar")
+ end
+
+ it "should throw an EOFError when there isn't enough data in the buffer" do
+ @buffer.reset_buffer("")
+ expect{@buffer.read(1)}.to raise_error(EOFError)
+
+ @buffer.reset_buffer("1234")
+ expect{@buffer.read(5)}.to raise_error(EOFError)
+ end
+ end
+
+ describe Thrift::IOStreamTransport do
+ before(:each) do
+ @input = double("Input", :closed? => false)
+ @output = double("Output", :closed? => false)
+ @trans = Thrift::IOStreamTransport.new(@input, @output)
+ end
+
+ it "should provide a reasonable to_s" do
+ expect(@input).to receive(:to_s).and_return("mock_input")
+ expect(@output).to receive(:to_s).and_return("mock_output")
+ expect(@trans.to_s).to eq("iostream(input=mock_input,output=mock_output)")
+ end
+
+ it "should be open as long as both input or output are open" do
+ expect(@trans).to be_open
+ allow(@input).to receive(:closed?).and_return(true)
+ expect(@trans).to be_open
+ allow(@input).to receive(:closed?).and_return(false)
+ allow(@output).to receive(:closed?).and_return(true)
+ expect(@trans).to be_open
+ allow(@input).to receive(:closed?).and_return(true)
+ expect(@trans).not_to be_open
+ end
+
+ it "should pass through read/write to input/output" do
+ expect(@input).to receive(:read).with(17).and_return("+ read")
+ expect(@output).to receive(:write).with("foobar").and_return("+ write")
+ expect(@trans.read(17)).to eq("+ read")
+ expect(@trans.write("foobar")).to eq("+ write")
+ end
+
+ it "should close both input and output when closed" do
+ expect(@input).to receive(:close)
+ expect(@output).to receive(:close)
+ @trans.close
+ end
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/spec/binary_protocol_accelerated_spec.rb b/src/jaegertracing/thrift/lib/rb/spec/binary_protocol_accelerated_spec.rb
new file mode 100644
index 000000000..b2cd04bec
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/spec/binary_protocol_accelerated_spec.rb
@@ -0,0 +1,46 @@
+#
+# 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
+ expect(Thrift::BinaryProtocolAcceleratedFactory.new.get_protocol(double("MockTransport"))).to be_instance_of(Thrift::BinaryProtocolAccelerated)
+ end
+
+ it "should provide a reasonable to_s" do
+ expect(Thrift::BinaryProtocolAcceleratedFactory.new.to_s).to eq("binary-accel")
+ end
+ end
+ end
+else
+ puts "skipping BinaryProtocolAccelerated spec because it is not defined."
+end
diff --git a/src/jaegertracing/thrift/lib/rb/spec/binary_protocol_spec.rb b/src/jaegertracing/thrift/lib/rb/spec/binary_protocol_spec.rb
new file mode 100644
index 000000000..065f5ce29
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/spec/binary_protocol_spec.rb
@@ -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.
+#
+
+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'))
+ expect(@prot).to receive(:read_string).and_return('testMessage')
+ expect(@prot.read_message_begin).to eq(['testMessage', Thrift::MessageTypes::REPLY, 42])
+ end
+
+ it "should raise an exception if the message header has the wrong version" do
+ expect(@prot).to receive(:read_i32).and_return(-1)
+ expect { @prot.read_message_begin }.to 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
+ expect(@prot).to receive(:read_i32).and_return(42)
+ expect(@prot).to receive(:strict_read).and_return(true)
+ expect { @prot.read_message_begin }.to raise_error(Thrift::ProtocolException, 'No version identifier, old protocol client?') do |e|
+ e.type == Thrift::ProtocolException::BAD_VERSION
+ end
+ end
+
+ it "should provide a reasonable to_s" do
+ expect(@prot.to_s).to eq("binary(memory)")
+ end
+ end
+
+ describe Thrift::BinaryProtocolFactory do
+ it "should create a BinaryProtocol" do
+ expect(Thrift::BinaryProtocolFactory.new.get_protocol(double("MockTransport"))).to be_instance_of(Thrift::BinaryProtocol)
+ end
+
+ it "should provide a reasonable to_s" do
+ expect(Thrift::BinaryProtocolFactory.new.to_s).to eq("binary")
+ end
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/spec/binary_protocol_spec_shared.rb b/src/jaegertracing/thrift/lib/rb/spec/binary_protocol_spec_shared.rb
new file mode 100644
index 000000000..58d65f040
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/spec/binary_protocol_spec_shared.rb
@@ -0,0 +1,458 @@
+# 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
+ expect(protocol_class.const_get(:VERSION_MASK)).to eq(0xffff0000)
+ expect(protocol_class.const_get(:VERSION_1)).to eq(0x80010000)
+ expect(protocol_class.const_get(:TYPE_MASK)).to eq(0x000000ff)
+ end
+
+ it "should make strict_read readable" do
+ expect(@prot.strict_read).to eql(true)
+ end
+
+ it "should make strict_write readable" do
+ expect(@prot.strict_write).to eql(true)
+ end
+
+ it "should write the message header" do
+ @prot.write_message_begin('testMessage', Thrift::MessageTypes::CALL, 17)
+ expect(@trans.read(@trans.available)).to eq([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)
+ expect(@trans.read(@trans.available)).to eq("\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)
+ expect(@trans.read(@trans.available)).to eq("\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)
+ expect(@trans.read(@trans.available)).to eq([Thrift::Types::DOUBLE, 3].pack("cn"))
+ end
+
+ # field footer is a noop
+
+ it "should write the STOP field" do
+ @prot.write_field_stop
+ expect(@trans.read(1)).to eq("\000")
+ end
+
+ it "should write the map header" do
+ @prot.write_map_begin(Thrift::Types::STRING, Thrift::Types::LIST, 17)
+ expect(@trans.read(@trans.available)).to eq([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)
+ expect(@trans.read(@trans.available)).to eq([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)
+ expect(@trans.read(@trans.available)).to eq([Thrift::Types::I16, 42].pack("cN"))
+ end
+
+ it "should write a bool" do
+ @prot.write_bool(true)
+ @prot.write_bool(false)
+ expect(@trans.read(@trans.available)).to eq("\001\000")
+ end
+
+ it "should treat a nil bool as false" do
+ @prot.write_bool(nil)
+ expect(@trans.read(1)).to eq("\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)
+ expect(@trans.read(1)).to eq([i].pack('c'))
+ end
+ end
+
+ it "should clip numbers out of signed range" do
+ (128..255).each do |i|
+ @prot.write_byte(i)
+ expect(@trans.read(1)).to eq([i].pack('c'))
+ end
+ end
+
+ it "errors out with a Bignum" do
+ expect { @prot.write_byte(2**65) }.to raise_error(RangeError)
+ end
+
+ it "should error gracefully when trying to write a nil byte" do
+ expect { @prot.write_byte(nil) }.to 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)
+
+ expect(@trans.read(@trans.available)).to eq("\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
+ expect { @prot.write_i16(nil) }.to 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
+ expect(@trans.read(@trans.available)).to eq("\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|
+ expect { @prot.write_i32(i) }.to raise_error(RangeError)
+ end
+ end
+
+ it "should error gracefully when trying to write a nil i32" do
+ expect { @prot.write_i32(nil) }.to 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
+ expect(@trans.read(@trans.available)).to eq(["\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(""))
+ expect { @prot.write_i64(2 ** 65 + 5) }.to raise_error(RangeError)
+ end
+
+ it "should error gracefully when trying to write a nil i64" do
+ expect { @prot.write_i64(nil) }.to 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)
+ expect(@trans.read(@trans.available)).to eq([f].pack("G"))
+ end
+ end
+
+ it "should error gracefully when trying to write a nil double" do
+ expect { @prot.write_double(nil) }.to 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)
+ expect(a.encoding).to eq(Encoding::BINARY)
+ expect(a.unpack('C*')).to eq([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)
+ expect(a.encoding).to eq(Encoding::BINARY)
+ expect(a.unpack('C*')).to eq([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)
+ expect(a.encoding).to eq(Encoding::BINARY)
+ expect(a.unpack('C*')).to eq([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)
+ expect(a.encoding).to eq(Encoding::BINARY)
+ expect(a.unpack('C*')).to eq([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)
+ expect(a.unpack('C*')).to eq([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)
+ expect(a.unpack('C*')).to eq([0x00, 0x00, 0x00, 0x04, 0x00, 0x01, 0x02, 0x03])
+ end
+ end
+
+ it "should error gracefully when trying to write a nil string" do
+ expect { @prot.write_string(nil) }.to 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)
+ expect(@trans.read(@trans.available)).to eq("\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)
+ expect(@trans.read(@trans.available)).to eq("\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"))
+ expect(@prot.read_field_begin).to eq([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"));
+ expect(@prot.read_field_begin).to eq([nil, Thrift::Types::STOP, 0])
+ end
+
+ it "should read a map header" do
+ @trans.write([Thrift::Types::DOUBLE, Thrift::Types::I64, 42].pack("ccN"))
+ expect(@prot.read_map_begin).to eq([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"))
+ expect(@prot.read_list_begin).to eq([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"))
+ expect(@prot.read_set_begin).to eq([Thrift::Types::STRING, 17])
+ end
+
+ # set footer is a noop
+
+ it "should read a bool" do
+ @trans.write("\001\000");
+ expect(@prot.read_bool).to eq(true)
+ expect(@prot.read_bool).to eq(false)
+ end
+
+ it "should read a byte" do
+ [-128, -57, -3, 0, 17, 24, 127].each do |i|
+ @trans.write([i].pack("c"))
+ expect(@prot.read_byte).to eq(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"));
+ expect(@prot.read_i16).to eq(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"))
+ expect(@prot.read_i32).to eq(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"))
+ expect(@prot.read_i64).to eq(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"));
+ expect(@prot.read_double).to eq(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
+ expect(a).to eq('abc'.encode('UTF-8'))
+ expect(a.encoding).to eq(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
+ expect(a).to eq("\u20AC".encode('UTF-8'))
+ expect(a.encoding).to eq(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
+ expect(a).to eq([0x00, 0x01, 0x02, 0x03].pack('C*'))
+ expect(a.encoding).to eq(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)
+ expect(@prot.read_string).to eq('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
+ expect(a).to eq([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| expect(client.recv_voidMethod).to eq(nil)}
+ )
+ end
+
+ it "should perform a complete rpc with a primitive return type" do
+ srv_test(
+ proc {|client| client.send_primitiveMethod()},
+ proc {|client| expect(client.recv_primitiveMethod).to eq(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
+ expect(result).to eq(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
diff --git a/src/jaegertracing/thrift/lib/rb/spec/bytes_spec.rb b/src/jaegertracing/thrift/lib/rb/spec/bytes_spec.rb
new file mode 100644
index 000000000..2e8653cfc
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/spec/bytes_spec.rb
@@ -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
+ expect(b.length).to eq(0)
+ expect(b.encoding).to eq(Encoding::BINARY)
+ end
+
+ it 'should create an empty buffer of given size' do
+ b = Thrift::Bytes.empty_byte_buffer 2
+ expect(b.length).to eq(2)
+ expect(b.getbyte(0)).to eq(0)
+ expect(b.getbyte(1)).to eq(0)
+ expect(b.encoding).to eq(Encoding::BINARY)
+ end
+ end
+
+ describe '.force_binary_encoding' do
+ it 'should change encoding' do
+ e = 'STRING'.encode('UTF-8')
+ expect(e.encoding).not_to eq(Encoding::BINARY)
+ a = Thrift::Bytes.force_binary_encoding e
+ expect(a.encoding).to eq(Encoding::BINARY)
+ end
+ end
+
+ describe '.get_string_byte' do
+ it 'should get the byte at index' do
+ s = "\x41\x42"
+ expect(Thrift::Bytes.get_string_byte(s, 0)).to eq(0x41)
+ expect(Thrift::Bytes.get_string_byte(s, 1)).to eq(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)
+ expect(s.getbyte(0)).to eq(0x43)
+ expect(s).to eq('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
+ expect(e.length).to eq(1)
+
+ a = Thrift::Bytes.convert_to_utf8_byte_buffer e
+ expect(a.encoding).to eq(Encoding::BINARY)
+ expect(a.length).to eq(3)
+ expect(a.unpack('C*')).to eq([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
+ expect(e.length).to eq(1)
+ expect(e.unpack('C*')).to eq([0xA4]) # euro sign is a different code point in ISO-8859-15
+
+ a = Thrift::Bytes.convert_to_utf8_byte_buffer e
+ expect(a.encoding).to eq(Encoding::BINARY)
+ expect(a.length).to eq(3)
+ expect(a.unpack('C*')).to eq([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*")
+ expect(e.encoding).to eq(Encoding::BINARY)
+ a = Thrift::Bytes.convert_to_string e
+ expect(a.encoding).to eq(Encoding::UTF_8)
+ expect(a).to eq("\u20AC")
+ end
+ end
+
+ else # RUBY_VERSION
+ describe '.empty_byte_buffer' do
+ it 'should create an empty buffer' do
+ b = Thrift::Bytes.empty_byte_buffer
+ expect(b.length).to eq(0)
+ end
+
+ it 'should create an empty buffer of given size' do
+ b = Thrift::Bytes.empty_byte_buffer 2
+ expect(b.length).to eq(2)
+ expect(b[0]).to eq(0)
+ expect(b[1]).to eq(0)
+ end
+ end
+
+ describe '.force_binary_encoding' do
+ it 'should be a no-op' do
+ e = 'STRING'
+ a = Thrift::Bytes.force_binary_encoding e
+ expect(a).to eq(e)
+ expect(a).to be(e)
+ end
+ end
+
+ describe '.get_string_byte' do
+ it 'should get the byte at index' do
+ s = "\x41\x42"
+ expect(Thrift::Bytes.get_string_byte(s, 0)).to eq(0x41)
+ expect(Thrift::Bytes.get_string_byte(s, 1)).to eq(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)
+ expect(s[0]).to eq(0x43)
+ expect(s).to eq('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
+ expect(a).to eq(e)
+ expect(a).to 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
+ expect(a).to eq(e)
+ expect(a).to be(e)
+ end
+ end
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/spec/client_spec.rb b/src/jaegertracing/thrift/lib/rb/spec/client_spec.rb
new file mode 100644
index 000000000..d5d4ceedb
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/spec/client_spec.rb
@@ -0,0 +1,98 @@
+#
+# 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 = double("MockProtocol")
+ @client = ClientSpec.new(@prot)
+ end
+
+ describe Thrift::Client do
+ it "should re-use iprot for oprot if not otherwise specified" do
+ expect(@client.instance_variable_get(:'@iprot')).to eql(@prot)
+ expect(@client.instance_variable_get(:'@oprot')).to eql(@prot)
+ end
+
+ it "should send a test message" do
+ expect(@prot).to receive(:write_message_begin).with('testMessage', Thrift::MessageTypes::CALL, 0)
+ mock_args = double('#<TestMessage_args:mock>')
+ expect(mock_args).to receive(:foo=).with('foo')
+ expect(mock_args).to receive(:bar=).with(42)
+ expect(mock_args).to receive(:write).with(@prot)
+ expect(@prot).to receive(:write_message_end)
+ expect(@prot).to receive(:trans) do
+ double('trans').tap do |trans|
+ expect(trans).to receive(:flush)
+ end
+ end
+ klass = double("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"
+ @prot.expect(:write_message_begin).with('testMessage', Thrift::MessageTypes::CALL, 0).ordered
+ @prot.expect(:write_message_begin).with('testMessage2', Thrift::MessageTypes::CALL, 1).ordered
+ @prot.expect(:write_message_begin).with('testMessage3', Thrift::MessageTypes::CALL, 2).ordered
+ @prot.stub!(:write_message_end)
+ @prot.stub!(:trans).and_return double("trans").as_null_object
+ @client.send_message('testMessage', double("args class").as_null_object)
+ @client.send_message('testMessage2', double("args class").as_null_object)
+ @client.send_message('testMessage3', double("args class").as_null_object)
+ end
+
+ it "should receive a test message" do
+ expect(@prot).to receive(:read_message_begin).and_return [nil, Thrift::MessageTypes::CALL, 0]
+ expect(@prot).to receive(:read_message_end)
+ mock_klass = double("#<MockClass:mock>")
+ expect(mock_klass).to receive(:read).with(@prot)
+ @client.receive_message(double("MockClass", :new => mock_klass))
+ end
+
+ it "should handle received exceptions" do
+ expect(@prot).to receive(:read_message_begin).and_return [nil, Thrift::MessageTypes::EXCEPTION, 0]
+ expect(@prot).to receive(:read_message_end)
+ expect(Thrift::ApplicationException).to receive(:new) do
+ StandardError.new.tap do |mock_exc|
+ expect(mock_exc).to receive(:read).with(@prot)
+ end
+ end
+ expect { @client.receive_message(nil) }.to raise_error(StandardError)
+ end
+
+ it "should close the transport if an error occurs while sending a message" do
+ allow(@prot).to receive(:write_message_begin)
+ expect(@prot).not_to receive(:write_message_end)
+ mock_args = double("#<TestMessage_args:mock>")
+ expect(mock_args).to receive(:write).with(@prot).and_raise(StandardError)
+ trans = double("MockTransport")
+ allow(@prot).to receive(:trans).and_return(trans)
+ expect(trans).to receive(:close)
+ klass = double("TestMessage_args", :new => mock_args)
+ expect { @client.send_message("testMessage", klass) }.to raise_error(StandardError)
+ end
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/spec/compact_protocol_spec.rb b/src/jaegertracing/thrift/lib/rb/spec/compact_protocol_spec.rb
new file mode 100644
index 000000000..513dd69cf
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/spec/compact_protocol_spec.rb
@@ -0,0 +1,158 @@
+# 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))
+ expect(read_back).to eq(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
+ expect(type).to eq(thrift_type)
+ expect(id).to eq(15)
+ read_back = proto.send(reader(primitive_type))
+ expect(read_back).to eq(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)
+ expect(struct2).to eq(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)
+ expect(client.recv_Janky).to eq(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)
+ expect(brcp2).to eq(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)
+ expect(struct).to eq(struct2)
+ end
+
+ it "should provide a reasonable to_s" do
+ trans = Thrift::MemoryBufferTransport.new
+ expect(Thrift::CompactProtocol.new(trans).to_s).to eq("compact(memory)")
+ 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
+
+describe Thrift::CompactProtocolFactory do
+ it "should create a CompactProtocol" do
+ expect(Thrift::CompactProtocolFactory.new.get_protocol(double("MockTransport"))).to be_instance_of(Thrift::CompactProtocol)
+ end
+
+ it "should provide a reasonable to_s" do
+ expect(Thrift::CompactProtocolFactory.new.to_s).to eq("compact")
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/spec/exception_spec.rb b/src/jaegertracing/thrift/lib/rb/spec/exception_spec.rb
new file mode 100644
index 000000000..379ae6980
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/spec/exception_spec.rb
@@ -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")
+ expect(e.message).to eq("test message")
+ end
+ end
+
+ describe Thrift::ApplicationException do
+ it "should inherit from Thrift::Exception" do
+ expect(Thrift::ApplicationException.superclass).to eq(Thrift::Exception)
+ end
+
+ it "should have an accessible type and message" do
+ e = Thrift::ApplicationException.new
+ expect(e.type).to eq(Thrift::ApplicationException::UNKNOWN)
+ expect(e.message).to be_nil
+ e = Thrift::ApplicationException.new(Thrift::ApplicationException::UNKNOWN_METHOD, "test message")
+ expect(e.type).to eq(Thrift::ApplicationException::UNKNOWN_METHOD)
+ expect(e.message).to eq("test message")
+ end
+
+ it "should read a struct off of a protocol" do
+ prot = double("MockProtocol")
+ expect(prot).to receive(:read_struct_begin).ordered
+ expect(prot).to receive(:read_field_begin).exactly(3).times.and_return(
+ ["message", Thrift::Types::STRING, 1],
+ ["type", Thrift::Types::I32, 2],
+ [nil, Thrift::Types::STOP, 0]
+ )
+ expect(prot).to receive(:read_string).ordered.and_return "test message"
+ expect(prot).to receive(:read_i32).ordered.and_return Thrift::ApplicationException::BAD_SEQUENCE_ID
+ expect(prot).to receive(:read_field_end).exactly(2).times
+ expect(prot).to receive(:read_struct_end).ordered
+
+ e = Thrift::ApplicationException.new
+ e.read(prot)
+ expect(e.message).to eq("test message")
+ expect(e.type).to eq(Thrift::ApplicationException::BAD_SEQUENCE_ID)
+ end
+
+ it "should skip bad fields when reading a struct" do
+ prot = double("MockProtocol")
+ expect(prot).to receive(:read_struct_begin).ordered
+ expect(prot).to 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]
+ )
+ expect(prot).to receive(:read_i32).and_return Thrift::ApplicationException::INVALID_MESSAGE_TYPE
+ expect(prot).to receive(:skip).with(Thrift::Types::STRING).twice
+ expect(prot).to receive(:skip).with(Thrift::Types::MAP)
+ expect(prot).to receive(:read_field_end).exactly(4).times
+ expect(prot).to receive(:read_struct_end).ordered
+
+ e = Thrift::ApplicationException.new
+ e.read(prot)
+ expect(e.message).to be_nil
+ expect(e.type).to eq(Thrift::ApplicationException::INVALID_MESSAGE_TYPE)
+ end
+
+ it "should write a Thrift::ApplicationException struct to the oprot" do
+ prot = double("MockProtocol")
+ expect(prot).to receive(:write_struct_begin).with("Thrift::ApplicationException").ordered
+ expect(prot).to receive(:write_field_begin).with("message", Thrift::Types::STRING, 1).ordered
+ expect(prot).to receive(:write_string).with("test message").ordered
+ expect(prot).to receive(:write_field_begin).with("type", Thrift::Types::I32, 2).ordered
+ expect(prot).to receive(:write_i32).with(Thrift::ApplicationException::UNKNOWN_METHOD).ordered
+ expect(prot).to receive(:write_field_end).twice
+ expect(prot).to receive(:write_field_stop).ordered
+ expect(prot).to 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 = double("MockProtocol")
+ expect(prot).to receive(:write_struct_begin).with("Thrift::ApplicationException").ordered
+ expect(prot).to receive(:write_field_begin).with("message", Thrift::Types::STRING, 1).ordered
+ expect(prot).to receive(:write_string).with("test message").ordered
+ expect(prot).to receive(:write_field_end).ordered
+ expect(prot).to receive(:write_field_stop).ordered
+ expect(prot).to receive(:write_struct_end).ordered
+
+ e = Thrift::ApplicationException.new(nil, "test message")
+ e.write(prot)
+
+ prot = double("MockProtocol")
+ expect(prot).to receive(:write_struct_begin).with("Thrift::ApplicationException").ordered
+ expect(prot).to receive(:write_field_begin).with("type", Thrift::Types::I32, 2).ordered
+ expect(prot).to receive(:write_i32).with(Thrift::ApplicationException::BAD_SEQUENCE_ID).ordered
+ expect(prot).to receive(:write_field_end).ordered
+ expect(prot).to receive(:write_field_stop).ordered
+ expect(prot).to receive(:write_struct_end).ordered
+
+ e = Thrift::ApplicationException.new(Thrift::ApplicationException::BAD_SEQUENCE_ID)
+ e.write(prot)
+
+ prot = double("MockProtocol")
+ expect(prot).to receive(:write_struct_begin).with("Thrift::ApplicationException").ordered
+ expect(prot).to receive(:write_field_stop).ordered
+ expect(prot).to 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")
+ expect(prot.type).to eq(Thrift::ProtocolException::SIZE_LIMIT)
+ expect(prot.message).to eq("message")
+ end
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/spec/flat_spec.rb b/src/jaegertracing/thrift/lib/rb/spec/flat_spec.rb
new file mode 100644
index 000000000..893056c10
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/spec/flat_spec.rb
@@ -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|
+ expect(File.exist?(File.join(prefix, name))).not_to be_truthy
+ 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|
+ expect(File.exist?(File.join(prefix, name))).to be_truthy
+ end
+ end
+
+ it "has a service class in the right place" do
+ expect(defined?(NamespacedSpecNamespace::NamespacedNonblockingService)).to be_truthy
+ end
+
+ it "has a struct in the right place" do
+ expect(defined?(NamespacedSpecNamespace::Hello)).to be_truthy
+ end
+
+ it "required an included file" do
+ expect(defined?(OtherNamespace::SomeEnum)).to be_truthy
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/spec/http_client_spec.rb b/src/jaegertracing/thrift/lib/rb/spec/http_client_spec.rb
new file mode 100644
index 000000000..df472ab33
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/spec/http_client_spec.rb
@@ -0,0 +1,139 @@
+#
+# 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::HTTPClientTransport' do
+
+ describe Thrift::HTTPClientTransport do
+ before(:each) do
+ @client = Thrift::HTTPClientTransport.new("http://my.domain.com/path/to/service?param=value")
+ end
+
+ it "should provide a reasonable to_s" do
+ @client.to_s == "http://my.domain.com/path/to/service?param=value"
+ end
+
+ it "should always be open" do
+ expect(@client).to be_open
+ @client.close
+ expect(@client).to be_open
+ end
+
+ it "should post via HTTP and return the results" do
+ @client.write "a test"
+ @client.write " frame"
+ expect(Net::HTTP).to receive(:new).with("my.domain.com", 80) do
+ double("Net::HTTP").tap do |http|
+ expect(http).to receive(:use_ssl=).with(false)
+ expect(http).to receive(:post).with("/path/to/service?param=value", "a test frame", {"Content-Type"=>"application/x-thrift"}) do
+ double("Net::HTTPOK").tap do |response|
+ expect(response).to receive(:body).and_return "data"
+ end
+ end
+ end
+ end
+ @client.flush
+ expect(@client.read(10)).to eq("data")
+ end
+
+ it "should send custom headers if defined" do
+ @client.write "test"
+ custom_headers = {"Cookie" => "Foo"}
+ headers = {"Content-Type"=>"application/x-thrift"}.merge(custom_headers)
+
+ @client.add_headers(custom_headers)
+ expect(Net::HTTP).to receive(:new).with("my.domain.com", 80) do
+ double("Net::HTTP").tap do |http|
+ expect(http).to receive(:use_ssl=).with(false)
+ expect(http).to receive(:post).with("/path/to/service?param=value", "test", headers) do
+ double("Net::HTTPOK").tap do |response|
+ expect(response).to receive(:body).and_return "data"
+ end
+ end
+ end
+ end
+ @client.flush
+ end
+
+ it 'should reset the outbuf on HTTP failures' do
+ @client.write "test"
+
+ expect(Net::HTTP).to receive(:new).with("my.domain.com", 80) do
+ double("Net::HTTP").tap do |http|
+ expect(http).to receive(:use_ssl=).with(false)
+ expect(http).to receive(:post).with("/path/to/service?param=value", "test", {"Content-Type"=>"application/x-thrift"}) { raise Net::ReadTimeout }
+ end
+ end
+
+ @client.flush rescue
+ expect(@client.instance_variable_get(:@outbuf)).to eq(Thrift::Bytes.empty_byte_buffer)
+ end
+
+ end
+
+ describe 'ssl enabled' do
+ before(:each) do
+ @service_path = "/path/to/service?param=value"
+ @server_uri = "https://my.domain.com"
+ end
+
+ it "should use SSL for https" do
+ client = Thrift::HTTPClientTransport.new("#{@server_uri}#{@service_path}")
+
+ client.write "test"
+
+ expect(Net::HTTP).to receive(:new).with("my.domain.com", 443) do
+ double("Net::HTTP").tap do |http|
+ expect(http).to receive(:use_ssl=).with(true)
+ expect(http).to receive(:verify_mode=).with(OpenSSL::SSL::VERIFY_PEER)
+ expect(http).to receive(:post).with(@service_path, "test",
+ "Content-Type" => "application/x-thrift") do
+ double("Net::HTTPOK").tap do |response|
+ expect(response).to receive(:body).and_return "data"
+ end
+ end
+ end
+ end
+ client.flush
+ expect(client.read(4)).to eq("data")
+ end
+
+ it "should set SSL verify mode when specified" do
+ client = Thrift::HTTPClientTransport.new("#{@server_uri}#{@service_path}",
+ :ssl_verify_mode => OpenSSL::SSL::VERIFY_NONE)
+
+ client.write "test"
+ expect(Net::HTTP).to receive(:new).with("my.domain.com", 443) do
+ double("Net::HTTP").tap do |http|
+ expect(http).to receive(:use_ssl=).with(true)
+ expect(http).to receive(:verify_mode=).with(OpenSSL::SSL::VERIFY_NONE)
+ expect(http).to receive(:post).with(@service_path, "test",
+ "Content-Type" => "application/x-thrift") do
+ double("Net::HTTPOK").tap do |response|
+ expect(response).to receive(:body).and_return "data"
+ end
+ end
+ end
+ end
+ client.flush
+ expect(client.read(4)).to eq("data")
+ end
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/spec/json_protocol_spec.rb b/src/jaegertracing/thrift/lib/rb/spec/json_protocol_spec.rb
new file mode 100644
index 000000000..fe1af7bb2
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/spec/json_protocol_spec.rb
@@ -0,0 +1,552 @@
+# 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 'JsonProtocol' do
+
+ describe Thrift::JsonProtocol do
+ before(:each) do
+ @trans = Thrift::MemoryBufferTransport.new
+ @prot = Thrift::JsonProtocol.new(@trans)
+ end
+
+ it "should write json escaped char" do
+ @prot.write_json_escape_char("\n")
+ expect(@trans.read(@trans.available)).to eq('\u000a')
+
+ @prot.write_json_escape_char(" ")
+ expect(@trans.read(@trans.available)).to eq('\u0020')
+ end
+
+ it "should write json char" do
+ @prot.write_json_char("\n")
+ expect(@trans.read(@trans.available)).to eq('\\n')
+
+ @prot.write_json_char(" ")
+ expect(@trans.read(@trans.available)).to eq(' ')
+
+ @prot.write_json_char("\\")
+ expect(@trans.read(@trans.available)).to eq("\\\\")
+
+ @prot.write_json_char("@")
+ expect(@trans.read(@trans.available)).to eq('@')
+ end
+
+ it "should write json string" do
+ @prot.write_json_string("this is a \\ json\nstring")
+ expect(@trans.read(@trans.available)).to eq("\"this is a \\\\ json\\nstring\"")
+ end
+
+ it "should write json base64" do
+ @prot.write_json_base64("this is a base64 string")
+ expect(@trans.read(@trans.available)).to eq("\"dGhpcyBpcyBhIGJhc2U2NCBzdHJpbmc=\"")
+ end
+
+ it "should write json integer" do
+ @prot.write_json_integer(45)
+ expect(@trans.read(@trans.available)).to eq("45")
+
+ @prot.write_json_integer(33000)
+ expect(@trans.read(@trans.available)).to eq("33000")
+
+ @prot.write_json_integer(3000000000)
+ expect(@trans.read(@trans.available)).to eq("3000000000")
+
+ @prot.write_json_integer(6000000000)
+ expect(@trans.read(@trans.available)).to eq("6000000000")
+ end
+
+ it "should write json double" do
+ @prot.write_json_double(12.3)
+ expect(@trans.read(@trans.available)).to eq("12.3")
+
+ @prot.write_json_double(-3.21)
+ expect(@trans.read(@trans.available)).to eq("-3.21")
+
+ @prot.write_json_double(((+1.0/0.0)/(+1.0/0.0)))
+ expect(@trans.read(@trans.available)).to eq("\"NaN\"")
+
+ @prot.write_json_double((+1.0/0.0))
+ expect(@trans.read(@trans.available)).to eq("\"Infinity\"")
+
+ @prot.write_json_double((-1.0/0.0))
+ expect(@trans.read(@trans.available)).to eq("\"-Infinity\"")
+ end
+
+ it "should write json object start" do
+ @prot.write_json_object_start
+ expect(@trans.read(@trans.available)).to eq("{")
+ end
+
+ it "should write json object end" do
+ @prot.write_json_object_end
+ expect(@trans.read(@trans.available)).to eq("}")
+ end
+
+ it "should write json array start" do
+ @prot.write_json_array_start
+ expect(@trans.read(@trans.available)).to eq("[")
+ end
+
+ it "should write json array end" do
+ @prot.write_json_array_end
+ expect(@trans.read(@trans.available)).to eq("]")
+ end
+
+ it "should write message begin" do
+ @prot.write_message_begin("name", 12, 32)
+ expect(@trans.read(@trans.available)).to eq("[1,\"name\",12,32")
+ end
+
+ it "should write message end" do
+ @prot.write_message_end
+ expect(@trans.read(@trans.available)).to eq("]")
+ end
+
+ it "should write struct begin" do
+ @prot.write_struct_begin("name")
+ expect(@trans.read(@trans.available)).to eq("{")
+ end
+
+ it "should write struct end" do
+ @prot.write_struct_end
+ expect(@trans.read(@trans.available)).to eq("}")
+ end
+
+ it "should write field begin" do
+ @prot.write_field_begin("name", Thrift::Types::STRUCT, 32)
+ expect(@trans.read(@trans.available)).to eq("32{\"rec\"")
+ end
+
+ it "should write field end" do
+ @prot.write_field_end
+ expect(@trans.read(@trans.available)).to eq("}")
+ end
+
+ it "should write field stop" do
+ @prot.write_field_stop
+ expect(@trans.read(@trans.available)).to eq("")
+ end
+
+ it "should write map begin" do
+ @prot.write_map_begin(Thrift::Types::STRUCT, Thrift::Types::LIST, 32)
+ expect(@trans.read(@trans.available)).to eq("[\"rec\",\"lst\",32,{")
+ end
+
+ it "should write map end" do
+ @prot.write_map_end
+ expect(@trans.read(@trans.available)).to eq("}]")
+ end
+
+ it "should write list begin" do
+ @prot.write_list_begin(Thrift::Types::STRUCT, 32)
+ expect(@trans.read(@trans.available)).to eq("[\"rec\",32")
+ end
+
+ it "should write list end" do
+ @prot.write_list_end
+ expect(@trans.read(@trans.available)).to eq("]")
+ end
+
+ it "should write set begin" do
+ @prot.write_set_begin(Thrift::Types::STRUCT, 32)
+ expect(@trans.read(@trans.available)).to eq("[\"rec\",32")
+ end
+
+ it "should write set end" do
+ @prot.write_set_end
+ expect(@trans.read(@trans.available)).to eq("]")
+ end
+
+ it "should write bool" do
+ @prot.write_bool(true)
+ expect(@trans.read(@trans.available)).to eq("1")
+
+ @prot.write_bool(false)
+ expect(@trans.read(@trans.available)).to eq("0")
+ end
+
+ it "should write byte" do
+ @prot.write_byte(100)
+ expect(@trans.read(@trans.available)).to eq("100")
+ end
+
+ it "should write i16" do
+ @prot.write_i16(1000)
+ expect(@trans.read(@trans.available)).to eq("1000")
+ end
+
+ it "should write i32" do
+ @prot.write_i32(3000000000)
+ expect(@trans.read(@trans.available)).to eq("3000000000")
+ end
+
+ it "should write i64" do
+ @prot.write_i64(6000000000)
+ expect(@trans.read(@trans.available)).to eq("6000000000")
+ end
+
+ it "should write double" do
+ @prot.write_double(1.23)
+ expect(@trans.read(@trans.available)).to eq("1.23")
+
+ @prot.write_double(-32.1)
+ expect(@trans.read(@trans.available)).to eq("-32.1")
+
+ @prot.write_double(((+1.0/0.0)/(+1.0/0.0)))
+ expect(@trans.read(@trans.available)).to eq("\"NaN\"")
+
+ @prot.write_double((+1.0/0.0))
+ expect(@trans.read(@trans.available)).to eq("\"Infinity\"")
+
+ @prot.write_double((-1.0/0.0))
+ expect(@trans.read(@trans.available)).to eq("\"-Infinity\"")
+ end
+
+ if RUBY_VERSION >= '1.9'
+ it 'should write string' do
+ @prot.write_string('this is a test string')
+ a = @trans.read(@trans.available)
+ expect(a).to eq('"this is a test string"'.force_encoding(Encoding::BINARY))
+ expect(a.encoding).to eq(Encoding::BINARY)
+ end
+
+ it 'should write string with unicode characters' do
+ @prot.write_string("this is a test string with unicode characters: \u20AC \u20AD")
+ a = @trans.read(@trans.available)
+ expect(a).to eq("\"this is a test string with unicode characters: \u20AC \u20AD\"".force_encoding(Encoding::BINARY))
+ expect(a.encoding).to eq(Encoding::BINARY)
+ end
+ else
+ it 'should write string' do
+ @prot.write_string('this is a test string')
+ expect(@trans.read(@trans.available)).to eq('"this is a test string"')
+ end
+ end
+
+ it "should write binary" do
+ @prot.write_binary("this is a base64 string")
+ expect(@trans.read(@trans.available)).to eq("\"dGhpcyBpcyBhIGJhc2U2NCBzdHJpbmc=\"")
+ end
+
+ it "should write long binary" do
+ @prot.write_binary((0...256).to_a.pack('C*'))
+ expect(@trans.read(@trans.available)).to eq("\"AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn+AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq+wsbKztLW2t7i5uru8vb6/wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t/g4eLj5OXm5+jp6uvs7e7v8PHy8/T19vf4+fr7/P3+/w==\"")
+ end
+
+ it "should get type name for type id" do
+ expect {@prot.get_type_name_for_type_id(Thrift::Types::STOP)}.to raise_error(NotImplementedError)
+ expect {@prot.get_type_name_for_type_id(Thrift::Types::VOID)}.to raise_error(NotImplementedError)
+ expect(@prot.get_type_name_for_type_id(Thrift::Types::BOOL)).to eq("tf")
+ expect(@prot.get_type_name_for_type_id(Thrift::Types::BYTE)).to eq("i8")
+ expect(@prot.get_type_name_for_type_id(Thrift::Types::DOUBLE)).to eq("dbl")
+ expect(@prot.get_type_name_for_type_id(Thrift::Types::I16)).to eq("i16")
+ expect(@prot.get_type_name_for_type_id(Thrift::Types::I32)).to eq("i32")
+ expect(@prot.get_type_name_for_type_id(Thrift::Types::I64)).to eq("i64")
+ expect(@prot.get_type_name_for_type_id(Thrift::Types::STRING)).to eq("str")
+ expect(@prot.get_type_name_for_type_id(Thrift::Types::STRUCT)).to eq("rec")
+ expect(@prot.get_type_name_for_type_id(Thrift::Types::MAP)).to eq("map")
+ expect(@prot.get_type_name_for_type_id(Thrift::Types::SET)).to eq("set")
+ expect(@prot.get_type_name_for_type_id(Thrift::Types::LIST)).to eq("lst")
+ end
+
+ it "should get type id for type name" do
+ expect {@prot.get_type_id_for_type_name("pp")}.to raise_error(NotImplementedError)
+ expect(@prot.get_type_id_for_type_name("tf")).to eq(Thrift::Types::BOOL)
+ expect(@prot.get_type_id_for_type_name("i8")).to eq(Thrift::Types::BYTE)
+ expect(@prot.get_type_id_for_type_name("dbl")).to eq(Thrift::Types::DOUBLE)
+ expect(@prot.get_type_id_for_type_name("i16")).to eq(Thrift::Types::I16)
+ expect(@prot.get_type_id_for_type_name("i32")).to eq(Thrift::Types::I32)
+ expect(@prot.get_type_id_for_type_name("i64")).to eq(Thrift::Types::I64)
+ expect(@prot.get_type_id_for_type_name("str")).to eq(Thrift::Types::STRING)
+ expect(@prot.get_type_id_for_type_name("rec")).to eq(Thrift::Types::STRUCT)
+ expect(@prot.get_type_id_for_type_name("map")).to eq(Thrift::Types::MAP)
+ expect(@prot.get_type_id_for_type_name("set")).to eq(Thrift::Types::SET)
+ expect(@prot.get_type_id_for_type_name("lst")).to eq(Thrift::Types::LIST)
+ end
+
+ it "should read json syntax char" do
+ @trans.write('F')
+ expect {@prot.read_json_syntax_char('G')}.to raise_error(Thrift::ProtocolException)
+ @trans.write('H')
+ @prot.read_json_syntax_char('H')
+ end
+
+ it "should read json escape char" do
+ @trans.write('0054')
+ expect(@prot.read_json_escape_char).to eq('T')
+
+ @trans.write("\"\\\"\"")
+ expect(@prot.read_json_string(false)).to eq("\"")
+
+ @trans.write("\"\\\\\"")
+ expect(@prot.read_json_string(false)).to eq("\\")
+
+ @trans.write("\"\\/\"")
+ expect(@prot.read_json_string(false)).to eq("\/")
+
+ @trans.write("\"\\b\"")
+ expect(@prot.read_json_string(false)).to eq("\b")
+
+ @trans.write("\"\\f\"")
+ expect(@prot.read_json_string(false)).to eq("\f")
+
+ @trans.write("\"\\n\"")
+ expect(@prot.read_json_string(false)).to eq("\n")
+
+ @trans.write("\"\\r\"")
+ expect(@prot.read_json_string(false)).to eq("\r")
+
+ @trans.write("\"\\t\"")
+ expect(@prot.read_json_string(false)).to eq("\t")
+ end
+
+ it "should read json string" do
+ @trans.write("\"\\P")
+ expect {@prot.read_json_string(false)}.to raise_error(Thrift::ProtocolException)
+
+ @trans.write("\"this is a test string\"")
+ expect(@prot.read_json_string).to eq("this is a test string")
+ end
+
+ it "should read json base64" do
+ @trans.write("\"dGhpcyBpcyBhIHRlc3Qgc3RyaW5n\"")
+ expect(@prot.read_json_base64).to eq("this is a test string")
+ end
+
+ it "should is json numeric" do
+ expect(@prot.is_json_numeric("A")).to eq(false)
+ expect(@prot.is_json_numeric("+")).to eq(true)
+ expect(@prot.is_json_numeric("-")).to eq(true)
+ expect(@prot.is_json_numeric(".")).to eq(true)
+ expect(@prot.is_json_numeric("0")).to eq(true)
+ expect(@prot.is_json_numeric("1")).to eq(true)
+ expect(@prot.is_json_numeric("2")).to eq(true)
+ expect(@prot.is_json_numeric("3")).to eq(true)
+ expect(@prot.is_json_numeric("4")).to eq(true)
+ expect(@prot.is_json_numeric("5")).to eq(true)
+ expect(@prot.is_json_numeric("6")).to eq(true)
+ expect(@prot.is_json_numeric("7")).to eq(true)
+ expect(@prot.is_json_numeric("8")).to eq(true)
+ expect(@prot.is_json_numeric("9")).to eq(true)
+ expect(@prot.is_json_numeric("E")).to eq(true)
+ expect(@prot.is_json_numeric("e")).to eq(true)
+ end
+
+ it "should read json numeric chars" do
+ @trans.write("1.453E45T")
+ expect(@prot.read_json_numeric_chars).to eq("1.453E45")
+ end
+
+ it "should read json integer" do
+ @trans.write("1.45\"\"")
+ expect {@prot.read_json_integer}.to raise_error(Thrift::ProtocolException)
+ @prot.read_string
+
+ @trans.write("1453T")
+ expect(@prot.read_json_integer).to eq(1453)
+ end
+
+ it "should read json double" do
+ @trans.write("1.45e3e01\"\"")
+ expect {@prot.read_json_double}.to raise_error(Thrift::ProtocolException)
+ @prot.read_string
+
+ @trans.write("\"1.453e01\"")
+ expect {@prot.read_json_double}.to raise_error(Thrift::ProtocolException)
+
+ @trans.write("1.453e01\"\"")
+ expect(@prot.read_json_double).to eq(14.53)
+ @prot.read_string
+
+ @trans.write("\"NaN\"")
+ expect(@prot.read_json_double.nan?).to eq(true)
+
+ @trans.write("\"Infinity\"")
+ expect(@prot.read_json_double).to eq(+1.0/0.0)
+
+ @trans.write("\"-Infinity\"")
+ expect(@prot.read_json_double).to eq(-1.0/0.0)
+ end
+
+ it "should read json object start" do
+ @trans.write("{")
+ expect(@prot.read_json_object_start).to eq(nil)
+ end
+
+ it "should read json object end" do
+ @trans.write("}")
+ expect(@prot.read_json_object_end).to eq(nil)
+ end
+
+ it "should read json array start" do
+ @trans.write("[")
+ expect(@prot.read_json_array_start).to eq(nil)
+ end
+
+ it "should read json array end" do
+ @trans.write("]")
+ expect(@prot.read_json_array_end).to eq(nil)
+ end
+
+ it "should read_message_begin" do
+ @trans.write("[2,")
+ expect {@prot.read_message_begin}.to raise_error(Thrift::ProtocolException)
+
+ @trans.write("[1,\"name\",12,32\"\"")
+ expect(@prot.read_message_begin).to eq(["name", 12, 32])
+ end
+
+ it "should read message end" do
+ @trans.write("]")
+ expect(@prot.read_message_end).to eq(nil)
+ end
+
+ it "should read struct begin" do
+ @trans.write("{")
+ expect(@prot.read_struct_begin).to eq(nil)
+ end
+
+ it "should read struct end" do
+ @trans.write("}")
+ expect(@prot.read_struct_end).to eq(nil)
+ end
+
+ it "should read field begin" do
+ @trans.write("1{\"rec\"")
+ expect(@prot.read_field_begin).to eq([nil, 12, 1])
+ end
+
+ it "should read field end" do
+ @trans.write("}")
+ expect(@prot.read_field_end).to eq(nil)
+ end
+
+ it "should read map begin" do
+ @trans.write("[\"rec\",\"lst\",2,{")
+ expect(@prot.read_map_begin).to eq([12, 15, 2])
+ end
+
+ it "should read map end" do
+ @trans.write("}]")
+ expect(@prot.read_map_end).to eq(nil)
+ end
+
+ it "should read list begin" do
+ @trans.write("[\"rec\",2\"\"")
+ expect(@prot.read_list_begin).to eq([12, 2])
+ end
+
+ it "should read list end" do
+ @trans.write("]")
+ expect(@prot.read_list_end).to eq(nil)
+ end
+
+ it "should read set begin" do
+ @trans.write("[\"rec\",2\"\"")
+ expect(@prot.read_set_begin).to eq([12, 2])
+ end
+
+ it "should read set end" do
+ @trans.write("]")
+ expect(@prot.read_set_end).to eq(nil)
+ end
+
+ it "should read bool" do
+ @trans.write("0\"\"")
+ expect(@prot.read_bool).to eq(false)
+ @prot.read_string
+
+ @trans.write("1\"\"")
+ expect(@prot.read_bool).to eq(true)
+ end
+
+ it "should read byte" do
+ @trans.write("60\"\"")
+ expect(@prot.read_byte).to eq(60)
+ end
+
+ it "should read i16" do
+ @trans.write("1000\"\"")
+ expect(@prot.read_i16).to eq(1000)
+ end
+
+ it "should read i32" do
+ @trans.write("3000000000\"\"")
+ expect(@prot.read_i32).to eq(3000000000)
+ end
+
+ it "should read i64" do
+ @trans.write("6000000000\"\"")
+ expect(@prot.read_i64).to eq(6000000000)
+ end
+
+ it "should read double" do
+ @trans.write("12.23\"\"")
+ expect(@prot.read_double).to eq(12.23)
+ end
+
+ if RUBY_VERSION >= '1.9'
+ it 'should read string' do
+ @trans.write('"this is a test string"'.force_encoding(Encoding::BINARY))
+ a = @prot.read_string
+ expect(a).to eq('this is a test string')
+ expect(a.encoding).to eq(Encoding::UTF_8)
+ end
+
+ it 'should read string with unicode characters' do
+ @trans.write('"this is a test string with unicode characters: \u20AC \u20AD"'.force_encoding(Encoding::BINARY))
+ a = @prot.read_string
+ expect(a).to eq("this is a test string with unicode characters: \u20AC \u20AD")
+ expect(a.encoding).to eq(Encoding::UTF_8)
+ end
+ else
+ it 'should read string' do
+ @trans.write('"this is a test string"')
+ expect(@prot.read_string).to eq('this is a test string')
+ end
+ end
+
+ it "should read binary" do
+ @trans.write("\"dGhpcyBpcyBhIHRlc3Qgc3RyaW5n\"")
+ expect(@prot.read_binary).to eq("this is a test string")
+ end
+
+ it "should read long binary" do
+ @trans.write("\"AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn+AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq+wsbKztLW2t7i5uru8vb6/wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t/g4eLj5OXm5+jp6uvs7e7v8PHy8/T19vf4+fr7/P3+/w==\"")
+ expect(@prot.read_binary.bytes.to_a).to eq((0...256).to_a)
+ end
+
+ it "should provide a reasonable to_s" do
+ expect(@prot.to_s).to eq("json(memory)")
+ end
+ end
+
+ describe Thrift::JsonProtocolFactory do
+ it "should create a JsonProtocol" do
+ expect(Thrift::JsonProtocolFactory.new.get_protocol(double("MockTransport"))).to be_instance_of(Thrift::JsonProtocol)
+ end
+
+ it "should provide a reasonable to_s" do
+ expect(Thrift::JsonProtocolFactory.new.to_s).to eq("json")
+ end
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/spec/namespaced_spec.rb b/src/jaegertracing/thrift/lib/rb/spec/namespaced_spec.rb
new file mode 100644
index 000000000..4d6d369e5
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/spec/namespaced_spec.rb
@@ -0,0 +1,67 @@
+#
+# 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 'namespaced generation' do
+ before do
+ require 'namespaced_spec_namespace/namespaced_nonblocking_service'
+ end
+
+ it "generated the right files" do
+ prefix = File.expand_path("../gen-rb", __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|
+ expect(File.exist?(File.join(prefix, name))).to be_truthy
+ end
+ end
+
+ it "did not generate the wrong files" do
+ prefix = File.expand_path("../gen-rb", __FILE__)
+ ["namespaced_nonblocking_service.rb",
+ "thrift_namespaced_spec_constants.rb",
+ "thrift_namespaced_spec_types.rb",
+ "referenced_constants.rb",
+ "referenced_types.rb"
+ ].each do |name|
+ expect(File.exist?(File.join(prefix, name))).not_to be_truthy
+ end
+ end
+
+ it "has a service class in the right place" do
+ expect(defined?(NamespacedSpecNamespace::NamespacedNonblockingService)).to be_truthy
+ end
+
+ it "has a struct in the right place" do
+ expect(defined?(NamespacedSpecNamespace::Hello)).to be_truthy
+ end
+
+ it "required an included file" do
+ expect(defined?(OtherNamespace::SomeEnum)).to be_truthy
+ end
+
+ it "extended a service" do
+ require "extended/extended_service"
+ end
+
+end
diff --git a/src/jaegertracing/thrift/lib/rb/spec/nonblocking_server_spec.rb b/src/jaegertracing/thrift/lib/rb/spec/nonblocking_server_spec.rb
new file mode 100644
index 000000000..613d88390
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/spec/nonblocking_server_spec.rb
@@ -0,0 +1,263 @@
+#
+# 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 'NonblockingServer' do
+
+ class Handler
+ def initialize
+ @queue = Queue.new
+ end
+
+ attr_accessor :server
+
+ def greeting(english)
+ if english
+ SpecNamespace::Hello.new
+ else
+ SpecNamespace::Hello.new(:greeting => "Aloha!")
+ end
+ end
+
+ def block
+ @queue.pop
+ end
+
+ def unblock(n)
+ n.times { @queue.push true }
+ end
+
+ def sleep(time)
+ Kernel.sleep time
+ end
+
+ def shutdown
+ @server.shutdown(0, false)
+ end
+ end
+
+ class SpecTransport < Thrift::BaseTransport
+ def initialize(transport, queue)
+ @transport = transport
+ @queue = queue
+ @flushed = false
+ end
+
+ def open?
+ @transport.open?
+ end
+
+ def open
+ @transport.open
+ end
+
+ def close
+ @transport.close
+ end
+
+ def read(sz)
+ @transport.read(sz)
+ end
+
+ def write(buf,sz=nil)
+ @transport.write(buf, sz)
+ end
+
+ def flush
+ @queue.push :flushed unless @flushed or @queue.nil?
+ @flushed = true
+ @transport.flush
+ end
+ end
+
+ class SpecServerSocket < Thrift::ServerSocket
+ def initialize(host, port, queue)
+ super(host, port)
+ @queue = queue
+ end
+
+ def listen
+ super
+ @queue.push :listen
+ end
+ end
+
+ describe Thrift::NonblockingServer do
+ before(:each) do
+ @port = 43251
+ handler = Handler.new
+ processor = SpecNamespace::NonblockingService::Processor.new(handler)
+ queue = Queue.new
+ @transport = SpecServerSocket.new('localhost', @port, queue)
+ transport_factory = Thrift::FramedTransportFactory.new
+ logger = Logger.new(STDERR)
+ logger.level = Logger::WARN
+ @server = Thrift::NonblockingServer.new(processor, @transport, transport_factory, nil, 5, logger)
+ handler.server = @server
+ @server_thread = Thread.new(Thread.current) do |master_thread|
+ begin
+ @server.serve
+ rescue => e
+ p e
+ puts e.backtrace * "\n"
+ master_thread.raise e
+ end
+ end
+ queue.pop
+
+ @clients = []
+ @catch_exceptions = false
+ end
+
+ after(:each) do
+ @clients.each { |client, trans| trans.close }
+ # @server.shutdown(1)
+ @server_thread.kill
+ @transport.close
+ end
+
+ def setup_client(queue = nil)
+ transport = SpecTransport.new(Thrift::FramedTransport.new(Thrift::Socket.new('localhost', @port)), queue)
+ protocol = Thrift::BinaryProtocol.new(transport)
+ client = SpecNamespace::NonblockingService::Client.new(protocol)
+ transport.open
+ @clients << [client, transport]
+ client
+ end
+
+ def setup_client_thread(result)
+ queue = Queue.new
+ Thread.new do
+ begin
+ client = setup_client
+ while (cmd = queue.pop)
+ msg, *args = cmd
+ case msg
+ when :block
+ result << client.block
+ when :unblock
+ client.unblock(args.first)
+ when :hello
+ result << client.greeting(true) # ignore result
+ when :sleep
+ client.sleep(args[0] || 0.5)
+ result << :slept
+ when :shutdown
+ client.shutdown
+ when :exit
+ result << :done
+ break
+ end
+ end
+ @clients.each { |c,t| t.close and break if c == client } #close the transport
+ rescue => e
+ raise e unless @catch_exceptions
+ end
+ end
+ queue
+ end
+
+ it "should handle basic message passing" do
+ client = setup_client
+ expect(client.greeting(true)).to eq(SpecNamespace::Hello.new)
+ expect(client.greeting(false)).to eq(SpecNamespace::Hello.new(:greeting => 'Aloha!'))
+ @server.shutdown
+ end
+
+ it "should handle concurrent clients" do
+ queue = Queue.new
+ trans_queue = Queue.new
+ 4.times do
+ Thread.new(Thread.current) do |main_thread|
+ begin
+ queue.push setup_client(trans_queue).block
+ rescue => e
+ main_thread.raise e
+ end
+ end
+ end
+ 4.times { trans_queue.pop }
+ setup_client.unblock(4)
+ 4.times { expect(queue.pop).to be_truthy }
+ @server.shutdown
+ end
+
+ it "should handle messages from more than 5 long-lived connections" do
+ queues = []
+ result = Queue.new
+ 7.times do |i|
+ queues << setup_client_thread(result)
+ Thread.pass if i == 4 # give the server time to accept connections
+ end
+ client = setup_client
+ # block 4 connections
+ 4.times { |i| queues[i] << :block }
+ queues[4] << :hello
+ queues[5] << :hello
+ queues[6] << :hello
+ 3.times { expect(result.pop).to eq(SpecNamespace::Hello.new) }
+ expect(client.greeting(true)).to eq(SpecNamespace::Hello.new)
+ queues[5] << [:unblock, 4]
+ 4.times { expect(result.pop).to be_truthy }
+ queues[2] << :hello
+ expect(result.pop).to eq(SpecNamespace::Hello.new)
+ expect(client.greeting(false)).to eq(SpecNamespace::Hello.new(:greeting => 'Aloha!'))
+ 7.times { queues.shift << :exit }
+ expect(client.greeting(true)).to eq(SpecNamespace::Hello.new)
+ @server.shutdown
+ end
+
+ it "should shut down when asked" do
+ # connect first to ensure it's running
+ client = setup_client
+ client.greeting(false) # force a message pass
+ @server.shutdown
+ expect(@server_thread.join(2)).to be_an_instance_of(Thread)
+ end
+
+ it "should continue processing active messages when shutting down" do
+ result = Queue.new
+ client = setup_client_thread(result)
+ client << :sleep
+ sleep 0.1 # give the server time to start processing the client's message
+ @server.shutdown
+ expect(@server_thread.join(2)).to be_an_instance_of(Thread)
+ expect(result.pop).to eq(:slept)
+ end
+
+ it "should kill active messages when they don't expire while shutting down" do
+ result = Queue.new
+ client = setup_client_thread(result)
+ client << [:sleep, 10]
+ sleep 0.1 # start processing the client's message
+ @server.shutdown(1)
+ @catch_exceptions = true
+ expect(@server_thread.join(3)).not_to be_nil
+ expect(result).to be_empty
+ end
+
+ it "should allow shutting down in response to a message" do
+ client = setup_client
+ expect(client.greeting(true)).to eq(SpecNamespace::Hello.new)
+ client.shutdown
+ expect(@server_thread.join(2)).not_to be_nil
+ end
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/spec/processor_spec.rb b/src/jaegertracing/thrift/lib/rb/spec/processor_spec.rb
new file mode 100644
index 000000000..d30553f55
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/spec/processor_spec.rb
@@ -0,0 +1,80 @@
+#
+# 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 'Processor' do
+
+ class ProcessorSpec
+ include Thrift::Processor
+ end
+
+ describe Thrift::Processor do
+ before(:each) do
+ @processor = ProcessorSpec.new(double("MockHandler"))
+ @prot = double("MockProtocol")
+ end
+
+ def mock_trans(obj)
+ expect(obj).to receive(:trans).ordered do
+ double("trans").tap do |trans|
+ expect(trans).to receive(:flush).ordered
+ end
+ end
+ end
+
+ it "should call process_<message> when it receives that message" do
+ expect(@prot).to receive(:read_message_begin).ordered.and_return ['testMessage', Thrift::MessageTypes::CALL, 17]
+ expect(@processor).to receive(:process_testMessage).with(17, @prot, @prot).ordered
+ expect(@processor.process(@prot, @prot)).to eq(true)
+ end
+
+ it "should raise an ApplicationException when the received message cannot be processed" do
+ expect(@prot).to receive(:read_message_begin).ordered.and_return ['testMessage', Thrift::MessageTypes::CALL, 4]
+ expect(@prot).to receive(:skip).with(Thrift::Types::STRUCT).ordered
+ expect(@prot).to receive(:read_message_end).ordered
+ expect(@prot).to receive(:write_message_begin).with('testMessage', Thrift::MessageTypes::EXCEPTION, 4).ordered
+ e = double(Thrift::ApplicationException)
+ expect(e).to receive(:write).with(@prot).ordered
+ expect(Thrift::ApplicationException).to receive(:new).with(Thrift::ApplicationException::UNKNOWN_METHOD, "Unknown function testMessage").and_return(e)
+ expect(@prot).to receive(:write_message_end).ordered
+ mock_trans(@prot)
+ @processor.process(@prot, @prot)
+ end
+
+ it "should pass args off to the args class" do
+ args_class = double("MockArgsClass")
+ args = double("#<MockArgsClass:mock>").tap do |args|
+ expect(args).to receive(:read).with(@prot).ordered
+ end
+ expect(args_class).to receive(:new).and_return args
+ expect(@prot).to receive(:read_message_end).ordered
+ expect(@processor.read_args(@prot, args_class)).to eql(args)
+ end
+
+ it "should write out a reply when asked" do
+ expect(@prot).to receive(:write_message_begin).with('testMessage', Thrift::MessageTypes::REPLY, 23).ordered
+ result = double("MockResult")
+ expect(result).to receive(:write).with(@prot).ordered
+ expect(@prot).to receive(:write_message_end).ordered
+ mock_trans(@prot)
+ @processor.write_result(result, @prot, 'testMessage', 23)
+ end
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/spec/serializer_spec.rb b/src/jaegertracing/thrift/lib/rb/spec/serializer_spec.rb
new file mode 100644
index 000000000..2a7dc6db9
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/spec/serializer_spec.rb
@@ -0,0 +1,67 @@
+#
+# 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 'Serializer' do
+
+ describe Thrift::Serializer do
+ it "should serialize structs to binary by default" do
+ serializer = Thrift::Serializer.new(Thrift::BinaryProtocolAcceleratedFactory.new)
+ data = serializer.serialize(SpecNamespace::Hello.new(:greeting => "'Ello guv'nor!"))
+ expect(data).to eq("\x0B\x00\x01\x00\x00\x00\x0E'Ello guv'nor!\x00")
+ end
+
+ it "should serialize structs to the given protocol" do
+ protocol = Thrift::BaseProtocol.new(double("transport"))
+ expect(protocol).to receive(:write_struct_begin).with("SpecNamespace::Hello")
+ expect(protocol).to receive(:write_field_begin).with("greeting", Thrift::Types::STRING, 1)
+ expect(protocol).to receive(:write_string).with("Good day")
+ expect(protocol).to receive(:write_field_end)
+ expect(protocol).to receive(:write_field_stop)
+ expect(protocol).to receive(:write_struct_end)
+ protocol_factory = double("ProtocolFactory")
+ allow(protocol_factory).to receive(:get_protocol).and_return(protocol)
+ serializer = Thrift::Serializer.new(protocol_factory)
+ serializer.serialize(SpecNamespace::Hello.new(:greeting => "Good day"))
+ end
+ end
+
+ describe Thrift::Deserializer do
+ it "should deserialize structs from binary by default" do
+ deserializer = Thrift::Deserializer.new
+ data = "\x0B\x00\x01\x00\x00\x00\x0E'Ello guv'nor!\x00"
+ expect(deserializer.deserialize(SpecNamespace::Hello.new, data)).to eq(SpecNamespace::Hello.new(:greeting => "'Ello guv'nor!"))
+ end
+
+ it "should deserialize structs from the given protocol" do
+ protocol = Thrift::BaseProtocol.new(double("transport"))
+ expect(protocol).to receive(:read_struct_begin).and_return("SpecNamespace::Hello")
+ expect(protocol).to receive(:read_field_begin).and_return(["greeting", Thrift::Types::STRING, 1],
+ [nil, Thrift::Types::STOP, 0])
+ expect(protocol).to receive(:read_string).and_return("Good day")
+ expect(protocol).to receive(:read_field_end)
+ expect(protocol).to receive(:read_struct_end)
+ protocol_factory = double("ProtocolFactory")
+ allow(protocol_factory).to receive(:get_protocol).and_return(protocol)
+ deserializer = Thrift::Deserializer.new(protocol_factory)
+ expect(deserializer.deserialize(SpecNamespace::Hello.new, "")).to eq(SpecNamespace::Hello.new(:greeting => "Good day"))
+ end
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/spec/server_socket_spec.rb b/src/jaegertracing/thrift/lib/rb/spec/server_socket_spec.rb
new file mode 100644
index 000000000..ec9e55005
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/spec/server_socket_spec.rb
@@ -0,0 +1,84 @@
+#
+# 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__)}/socket_spec_shared")
+
+describe 'Thrift::ServerSocket' do
+
+ describe Thrift::ServerSocket do
+ before(:each) do
+ @socket = Thrift::ServerSocket.new(1234)
+ end
+
+ it "should create a handle when calling listen" do
+ expect(TCPServer).to receive(:new).with(nil, 1234)
+ @socket.listen
+ end
+
+ it "should accept an optional host argument" do
+ @socket = Thrift::ServerSocket.new('localhost', 1234)
+ expect(TCPServer).to receive(:new).with('localhost', 1234)
+ @socket.to_s == "server(localhost:1234)"
+ @socket.listen
+ end
+
+ it "should create a Thrift::Socket to wrap accepted sockets" do
+ handle = double("TCPServer")
+ expect(TCPServer).to receive(:new).with(nil, 1234).and_return(handle)
+ @socket.listen
+ sock = double("sock")
+ expect(handle).to receive(:accept).and_return(sock)
+ trans = double("Socket")
+ expect(Thrift::Socket).to receive(:new).and_return(trans)
+ expect(trans).to receive(:handle=).with(sock)
+ expect(@socket.accept).to eq(trans)
+ end
+
+ it "should close the handle when closed" do
+ handle = double("TCPServer", :closed? => false)
+ expect(TCPServer).to receive(:new).with(nil, 1234).and_return(handle)
+ @socket.listen
+ expect(handle).to receive(:close)
+ @socket.close
+ end
+
+ it "should return nil when accepting if there is no handle" do
+ expect(@socket.accept).to be_nil
+ end
+
+ it "should return true for closed? when appropriate" do
+ handle = double("TCPServer", :closed? => false)
+ allow(TCPServer).to receive(:new).and_return(handle)
+ @socket.listen
+ expect(@socket).not_to be_closed
+ allow(handle).to receive(:close)
+ @socket.close
+ expect(@socket).to be_closed
+ @socket.listen
+ expect(@socket).not_to be_closed
+ allow(handle).to receive(:closed?).and_return(true)
+ expect(@socket).to be_closed
+ end
+
+ it "should provide a reasonable to_s" do
+ expect(@socket.to_s).to eq("socket(:1234)")
+ end
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/spec/server_spec.rb b/src/jaegertracing/thrift/lib/rb/spec/server_spec.rb
new file mode 100644
index 000000000..57f523776
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/spec/server_spec.rb
@@ -0,0 +1,187 @@
+#
+# 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 'Server' do
+
+ describe Thrift::BaseServer do
+ before(:each) do
+ @processor = double("Processor")
+ @serverTrans = double("ServerTransport")
+ @trans = double("BaseTransport")
+ @prot = double("BaseProtocol")
+ @server = described_class.new(@processor, @serverTrans, @trans, @prot)
+ end
+
+ it "should default to BaseTransportFactory and BinaryProtocolFactory when not specified" do
+ @server = Thrift::BaseServer.new(double("Processor"), double("BaseServerTransport"))
+ expect(@server.instance_variable_get(:'@transport_factory')).to be_an_instance_of(Thrift::BaseTransportFactory)
+ expect(@server.instance_variable_get(:'@protocol_factory')).to be_an_instance_of(Thrift::BinaryProtocolFactory)
+ end
+
+ it "should not serve" do
+ expect { @server.serve()}.to raise_error(NotImplementedError)
+ end
+
+ it "should provide a reasonable to_s" do
+ expect(@serverTrans).to receive(:to_s).once.and_return("serverTrans")
+ expect(@trans).to receive(:to_s).once.and_return("trans")
+ expect(@prot).to receive(:to_s).once.and_return("prot")
+ expect(@server.to_s).to eq("server(prot(trans(serverTrans)))")
+ end
+ end
+
+ describe Thrift::SimpleServer do
+ before(:each) do
+ @processor = double("Processor")
+ @serverTrans = double("ServerTransport")
+ @trans = double("BaseTransport")
+ @prot = double("BaseProtocol")
+ @client = double("Client")
+ @server = described_class.new(@processor, @serverTrans, @trans, @prot)
+ end
+
+ it "should provide a reasonable to_s" do
+ expect(@serverTrans).to receive(:to_s).once.and_return("serverTrans")
+ expect(@trans).to receive(:to_s).once.and_return("trans")
+ expect(@prot).to receive(:to_s).once.and_return("prot")
+ expect(@server.to_s).to eq("simple(server(prot(trans(serverTrans))))")
+ end
+
+ it "should serve in the main thread" do
+ expect(@serverTrans).to receive(:listen).ordered
+ expect(@serverTrans).to receive(:accept).exactly(3).times.and_return(@client)
+ expect(@trans).to receive(:get_transport).exactly(3).times.with(@client).and_return(@trans)
+ expect(@prot).to receive(:get_protocol).exactly(3).times.with(@trans).and_return(@prot)
+ x = 0
+ expect(@processor).to receive(:process).exactly(3).times.with(@prot, @prot) do
+ case (x += 1)
+ when 1 then raise Thrift::TransportException
+ when 2 then raise Thrift::ProtocolException
+ when 3 then throw :stop
+ end
+ end
+ expect(@trans).to receive(:close).exactly(3).times
+ expect(@serverTrans).to receive(:close).ordered
+ expect { @server.serve }.to throw_symbol(:stop)
+ end
+ end
+
+ describe Thrift::ThreadedServer do
+ before(:each) do
+ @processor = double("Processor")
+ @serverTrans = double("ServerTransport")
+ @trans = double("BaseTransport")
+ @prot = double("BaseProtocol")
+ @client = double("Client")
+ @server = described_class.new(@processor, @serverTrans, @trans, @prot)
+ end
+
+ it "should provide a reasonable to_s" do
+ expect(@serverTrans).to receive(:to_s).once.and_return("serverTrans")
+ expect(@trans).to receive(:to_s).once.and_return("trans")
+ expect(@prot).to receive(:to_s).once.and_return("prot")
+ expect(@server.to_s).to eq("threaded(server(prot(trans(serverTrans))))")
+ end
+
+ it "should serve using threads" do
+ expect(@serverTrans).to receive(:listen).ordered
+ expect(@serverTrans).to receive(:accept).exactly(3).times.and_return(@client)
+ expect(@trans).to receive(:get_transport).exactly(3).times.with(@client).and_return(@trans)
+ expect(@prot).to receive(:get_protocol).exactly(3).times.with(@trans).and_return(@prot)
+ expect(Thread).to receive(:new).with(@prot, @trans).exactly(3).times.and_yield(@prot, @trans)
+ x = 0
+ expect(@processor).to receive(:process).exactly(3).times.with(@prot, @prot) do
+ case (x += 1)
+ when 1 then raise Thrift::TransportException
+ when 2 then raise Thrift::ProtocolException
+ when 3 then throw :stop
+ end
+ end
+ expect(@trans).to receive(:close).exactly(3).times
+ expect(@serverTrans).to receive(:close).ordered
+ expect { @server.serve }.to throw_symbol(:stop)
+ end
+ end
+
+ describe Thrift::ThreadPoolServer do
+ before(:each) do
+ @processor = double("Processor")
+ @server_trans = double("ServerTransport")
+ @trans = double("BaseTransport")
+ @prot = double("BaseProtocol")
+ @client = double("Client")
+ @server = described_class.new(@processor, @server_trans, @trans, @prot)
+ sleep(0.15)
+ end
+
+ it "should provide a reasonable to_s" do
+ expect(@server_trans).to receive(:to_s).once.and_return("server_trans")
+ expect(@trans).to receive(:to_s).once.and_return("trans")
+ expect(@prot).to receive(:to_s).once.and_return("prot")
+ expect(@server.to_s).to eq("threadpool(server(prot(trans(server_trans))))")
+ end
+
+ it "should serve inside a thread" do
+ exception_q = @server.instance_variable_get(:@exception_q)
+ expect_any_instance_of(described_class).to receive(:serve) do
+ exception_q.push(StandardError.new('ERROR'))
+ end
+ expect { @server.rescuable_serve }.to(raise_error('ERROR'))
+ sleep(0.15)
+ end
+
+ it "should avoid running the server twice when retrying rescuable_serve" do
+ exception_q = @server.instance_variable_get(:@exception_q)
+ expect_any_instance_of(described_class).to receive(:serve) do
+ exception_q.push(StandardError.new('ERROR1'))
+ exception_q.push(StandardError.new('ERROR2'))
+ end
+ expect { @server.rescuable_serve }.to(raise_error('ERROR1'))
+ expect { @server.rescuable_serve }.to(raise_error('ERROR2'))
+ end
+
+ it "should serve using a thread pool" do
+ thread_q = double("SizedQueue")
+ exception_q = double("Queue")
+ @server.instance_variable_set(:@thread_q, thread_q)
+ @server.instance_variable_set(:@exception_q, exception_q)
+ expect(@server_trans).to receive(:listen).ordered
+ expect(thread_q).to receive(:push).with(:token)
+ expect(thread_q).to receive(:pop)
+ expect(Thread).to receive(:new).and_yield
+ expect(@server_trans).to receive(:accept).exactly(3).times.and_return(@client)
+ expect(@trans).to receive(:get_transport).exactly(3).times.and_return(@trans)
+ expect(@prot).to receive(:get_protocol).exactly(3).times.and_return(@prot)
+ x = 0
+ error = RuntimeError.new("Stopped")
+ expect(@processor).to receive(:process).exactly(3).times.with(@prot, @prot) do
+ case (x += 1)
+ when 1 then raise Thrift::TransportException
+ when 2 then raise Thrift::ProtocolException
+ when 3 then raise error
+ end
+ end
+ expect(@trans).to receive(:close).exactly(3).times
+ expect(exception_q).to receive(:push).with(error).and_throw(:stop)
+ expect(@server_trans).to receive(:close)
+ expect { @server.serve }.to(throw_symbol(:stop))
+ end
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/spec/socket_spec.rb b/src/jaegertracing/thrift/lib/rb/spec/socket_spec.rb
new file mode 100644
index 000000000..202c745ea
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/spec/socket_spec.rb
@@ -0,0 +1,68 @@
+#
+# 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__)}/socket_spec_shared")
+
+describe 'Socket' do
+
+ describe Thrift::Socket do
+ before(:each) do
+ @socket = Thrift::Socket.new
+ @handle = double("Handle", :closed? => false)
+ allow(@handle).to receive(:close)
+ allow(@handle).to receive(:connect_nonblock)
+ allow(@handle).to receive(:setsockopt)
+ allow(::Socket).to receive(:new).and_return(@handle)
+ end
+
+ it_should_behave_like "a socket"
+
+ it "should raise a TransportException when it cannot open a socket" do
+ expect(::Socket).to receive(:getaddrinfo).with("localhost", 9090, nil, ::Socket::SOCK_STREAM).and_return([[]])
+ expect { @socket.open }.to raise_error(Thrift::TransportException) { |e| expect(e.type).to eq(Thrift::TransportException::NOT_OPEN) }
+ end
+
+ it "should open a ::Socket with default args" do
+ expect(::Socket).to receive(:new).and_return(double("Handle", :connect_nonblock => true, :setsockopt => nil))
+ expect(::Socket).to receive(:getaddrinfo).with("localhost", 9090, nil, ::Socket::SOCK_STREAM).and_return([[]])
+ expect(::Socket).to receive(:sockaddr_in)
+ @socket.to_s == "socket(localhost:9090)"
+ @socket.open
+ end
+
+ it "should accept host/port options" do
+ expect(::Socket).to receive(:new).and_return(double("Handle", :connect_nonblock => true, :setsockopt => nil))
+ expect(::Socket).to receive(:getaddrinfo).with("my.domain", 1234, nil, ::Socket::SOCK_STREAM).and_return([[]])
+ expect(::Socket).to receive(:sockaddr_in)
+ @socket = Thrift::Socket.new('my.domain', 1234).open
+ @socket.to_s == "socket(my.domain:1234)"
+ end
+
+ it "should accept an optional timeout" do
+ allow(::Socket).to receive(:new)
+ expect(Thrift::Socket.new('localhost', 8080, 5).timeout).to eq(5)
+ end
+
+ it "should provide a reasonable to_s" do
+ allow(::Socket).to receive(:new)
+ expect(Thrift::Socket.new('myhost', 8090).to_s).to eq("socket(myhost:8090)")
+ end
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/spec/socket_spec_shared.rb b/src/jaegertracing/thrift/lib/rb/spec/socket_spec_shared.rb
new file mode 100644
index 000000000..32bdb71f0
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/spec/socket_spec_shared.rb
@@ -0,0 +1,104 @@
+#
+# 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 socket" do
+ it "should open a socket" do
+ expect(@socket.open).to eq(@handle)
+ end
+
+ it "should be open whenever it has a handle" do
+ expect(@socket).not_to be_open
+ @socket.open
+ expect(@socket).to be_open
+ @socket.handle = nil
+ expect(@socket).not_to be_open
+ @socket.handle = @handle
+ @socket.close
+ expect(@socket).not_to be_open
+ end
+
+ it "should write data to the handle" do
+ @socket.open
+ expect(@handle).to receive(:write).with("foobar")
+ @socket.write("foobar")
+ expect(@handle).to receive(:write).with("fail").and_raise(StandardError)
+ expect { @socket.write("fail") }.to raise_error(Thrift::TransportException) { |e| expect(e.type).to eq(Thrift::TransportException::NOT_OPEN) }
+ end
+
+ it "should raise an error when it cannot read from the handle" do
+ @socket.open
+ expect(@handle).to receive(:readpartial).with(17).and_raise(StandardError)
+ expect { @socket.read(17) }.to raise_error(Thrift::TransportException) { |e| expect(e.type).to eq(Thrift::TransportException::NOT_OPEN) }
+ end
+
+ it "should return the data read when reading from the handle works" do
+ @socket.open
+ expect(@handle).to receive(:readpartial).with(17).and_return("test data")
+ expect(@socket.read(17)).to eq("test data")
+ end
+
+ it "should declare itself as closed when it has an error" do
+ @socket.open
+ expect(@handle).to receive(:write).with("fail").and_raise(StandardError)
+ expect(@socket).to be_open
+ expect { @socket.write("fail") }.to raise_error(Thrift::TransportException) { |e| expect(e.type).to eq(Thrift::TransportException::NOT_OPEN) }
+ expect(@socket).not_to be_open
+ end
+
+ it "should raise an error when the stream is closed" do
+ @socket.open
+ allow(@handle).to receive(:closed?).and_return(true)
+ expect(@socket).not_to be_open
+ expect { @socket.write("fail") }.to raise_error(IOError, "closed stream")
+ expect { @socket.read(10) }.to raise_error(IOError, "closed stream")
+ end
+
+ it "should support the timeout accessor for read" do
+ @socket.timeout = 3
+ @socket.open
+ expect(IO).to receive(:select).with([@handle], nil, nil, 3).and_return([[@handle], [], []])
+ expect(@handle).to receive(:readpartial).with(17).and_return("test data")
+ expect(@socket.read(17)).to eq("test data")
+ end
+
+ it "should support the timeout accessor for write" do
+ @socket.timeout = 3
+ @socket.open
+ expect(IO).to receive(:select).with(nil, [@handle], nil, 3).twice.and_return([[], [@handle], []])
+ expect(@handle).to receive(:write_nonblock).with("test data").and_return(4)
+ expect(@handle).to receive(:write_nonblock).with(" data").and_return(5)
+ expect(@socket.write("test data")).to eq(9)
+ end
+
+ it "should raise an error when read times out" do
+ @socket.timeout = 0.5
+ @socket.open
+ expect(IO).to receive(:select).once {sleep(0.5); nil}
+ expect { @socket.read(17) }.to raise_error(Thrift::TransportException) { |e| expect(e.type).to eq(Thrift::TransportException::TIMED_OUT) }
+ end
+
+ it "should raise an error when write times out" do
+ @socket.timeout = 0.5
+ @socket.open
+ allow(IO).to receive(:select).with(nil, [@handle], nil, 0.5).and_return(nil)
+ expect { @socket.write("test data") }.to raise_error(Thrift::TransportException) { |e| expect(e.type).to eq(Thrift::TransportException::TIMED_OUT) }
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/spec/spec_helper.rb b/src/jaegertracing/thrift/lib/rb/spec/spec_helper.rb
new file mode 100644
index 000000000..5bf98d077
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/spec/spec_helper.rb
@@ -0,0 +1,64 @@
+# 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 'rubygems'
+require 'rspec'
+
+$:.unshift File.join(File.dirname(__FILE__), *%w[.. ext])
+
+# pretend we already loaded fastthread, otherwise the nonblocking_server_spec
+# will get screwed up
+# $" << 'fastthread.bundle'
+
+require 'thrift'
+
+unless Object.method_defined? :tap
+ # if Object#tap isn't defined, then add it; this should only happen in Ruby < 1.8.7
+ class Object
+ def tap(&block)
+ block.call(self)
+ self
+ end
+ end
+end
+
+RSpec.configure do |configuration|
+ configuration.before(:each) do
+ Thrift.type_checking = true
+ end
+end
+
+$:.unshift File.join(File.dirname(__FILE__), *%w[.. test debug_proto gen-rb])
+require 'srv'
+require 'debug_proto_test_constants'
+
+$:.unshift File.join(File.dirname(__FILE__), *%w[gen-rb])
+require 'thrift_spec_types'
+require 'nonblocking_service'
+
+module Fixtures
+ COMPACT_PROTOCOL_TEST_STRUCT = Thrift::Test::COMPACT_TEST.dup
+ COMPACT_PROTOCOL_TEST_STRUCT.a_binary = [0,1,2,3,4,5,6,7,8].pack('c*')
+ COMPACT_PROTOCOL_TEST_STRUCT.set_byte_map = nil
+ COMPACT_PROTOCOL_TEST_STRUCT.map_byte_map = nil
+end
+
+$:.unshift File.join(File.dirname(__FILE__), *%w[gen-rb/flat])
+
diff --git a/src/jaegertracing/thrift/lib/rb/spec/ssl_server_socket_spec.rb b/src/jaegertracing/thrift/lib/rb/spec/ssl_server_socket_spec.rb
new file mode 100644
index 000000000..82e651843
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/spec/ssl_server_socket_spec.rb
@@ -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.
+#
+
+require 'spec_helper'
+require File.expand_path("#{File.dirname(__FILE__)}/socket_spec_shared")
+
+describe 'SSLServerSocket' do
+
+ describe Thrift::SSLServerSocket do
+ before(:each) do
+ @socket = Thrift::SSLServerSocket.new(1234)
+ end
+
+ it "should provide a reasonable to_s" do
+ expect(@socket.to_s).to eq("ssl(socket(:1234))")
+ end
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/spec/ssl_socket_spec.rb b/src/jaegertracing/thrift/lib/rb/spec/ssl_socket_spec.rb
new file mode 100644
index 000000000..808d8d512
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/spec/ssl_socket_spec.rb
@@ -0,0 +1,78 @@
+#
+# 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__)}/socket_spec_shared")
+
+describe 'SSLSocket' do
+
+ describe Thrift::SSLSocket do
+ before(:each) do
+ @context = OpenSSL::SSL::SSLContext.new
+ @socket = Thrift::SSLSocket.new
+ @simple_socket_handle = double("Handle", :closed? => false)
+ allow(@simple_socket_handle).to receive(:close)
+ allow(@simple_socket_handle).to receive(:connect_nonblock)
+ allow(@simple_socket_handle).to receive(:setsockopt)
+
+ @handle = double(double("SSLHandle", :connect_nonblock => true, :post_connection_check => true), :closed? => false)
+ allow(@handle).to receive(:connect_nonblock)
+ allow(@handle).to receive(:close)
+ allow(@handle).to receive(:post_connection_check)
+
+ allow(::Socket).to receive(:new).and_return(@simple_socket_handle)
+ allow(OpenSSL::SSL::SSLSocket).to receive(:new).and_return(@handle)
+ end
+
+ it_should_behave_like "a socket"
+
+ it "should raise a TransportException when it cannot open a ssl socket" do
+ expect(::Socket).to receive(:getaddrinfo).with("localhost", 9090, nil, ::Socket::SOCK_STREAM).and_return([[]])
+ expect { @socket.open }.to raise_error(Thrift::TransportException) { |e| expect(e.type).to eq(Thrift::TransportException::NOT_OPEN) }
+ end
+
+ it "should open a ::Socket with default args" do
+ expect(OpenSSL::SSL::SSLSocket).to receive(:new).with(@simple_socket_handle, nil).and_return(@handle)
+ expect(@handle).to receive(:post_connection_check).with('localhost')
+ @socket.open
+ end
+
+ it "should accept host/port options" do
+ handle = double("Handle", :connect_nonblock => true, :setsockopt => nil)
+ allow(::Socket).to receive(:new).and_return(handle)
+ expect(::Socket).to receive(:getaddrinfo).with("my.domain", 1234, nil, ::Socket::SOCK_STREAM).and_return([[]])
+ expect(::Socket).to receive(:sockaddr_in)
+ expect(OpenSSL::SSL::SSLSocket).to receive(:new).with(handle, nil).and_return(@handle)
+ expect(@handle).to receive(:post_connection_check).with('my.domain')
+ Thrift::SSLSocket.new('my.domain', 1234, 6000, nil).open
+ end
+
+ it "should accept an optional timeout" do
+ expect(Thrift::SSLSocket.new('localhost', 8080, 5).timeout).to eq(5)
+ end
+
+ it "should accept an optional context" do
+ expect(Thrift::SSLSocket.new('localhost', 8080, 5, @context).ssl_context).to eq(@context)
+ end
+
+ it "should provide a reasonable to_s" do
+ expect(Thrift::SSLSocket.new('myhost', 8090).to_s).to eq("ssl(socket(myhost:8090))")
+ end
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/spec/struct_nested_containers_spec.rb b/src/jaegertracing/thrift/lib/rb/spec/struct_nested_containers_spec.rb
new file mode 100644
index 000000000..d063569b5
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/spec/struct_nested_containers_spec.rb
@@ -0,0 +1,191 @@
+#
+# 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 'StructNestedContainers' do
+
+ def with_type_checking
+ saved_type_checking, Thrift.type_checking = Thrift.type_checking, true
+ begin
+ yield
+ ensure
+ Thrift.type_checking = saved_type_checking
+ end
+ end
+
+ describe Thrift::Struct do
+ # Nested container tests, see THRIFT-369.
+ it "should support nested lists inside lists" do
+ with_type_checking do
+ a, b = SpecNamespace::NestedListInList.new, SpecNamespace::NestedListInList.new
+ [a, b].each do |thrift_struct|
+ thrift_struct.value = [ [1, 2, 3], [2, 3, 4] ]
+ thrift_struct.validate
+ end
+ expect(a).to eq(b)
+ b.value.push [3, 4, 5]
+ expect(a).not_to eq(b)
+ end
+ end
+
+ it "should support nested lists inside sets" do
+ with_type_checking do
+ a, b = SpecNamespace::NestedListInSet.new, SpecNamespace::NestedListInSet.new
+ [a, b].each do |thrift_struct|
+ thrift_struct.value = [ [1, 2, 3], [2, 3, 4] ].to_set
+ thrift_struct.validate
+ end
+ expect(a).to eq(b)
+ b.value.add [3, 4, 5]
+ expect(a).not_to eq(b)
+ end
+ end
+
+ it "should support nested lists in map keys" do
+ with_type_checking do
+ a, b = SpecNamespace::NestedListInMapKey.new, SpecNamespace::NestedListInMapKey.new
+ [a, b].each do |thrift_struct|
+ thrift_struct.value = { [1, 2, 3] => 1, [2, 3, 4] => 2 }
+ thrift_struct.validate
+ end
+ expect(a).to eq(b)
+ b.value[[3, 4, 5]] = 3
+ expect(a).not_to eq(b)
+ end
+ end
+
+ it "should support nested lists in map values" do
+ with_type_checking do
+ a, b = SpecNamespace::NestedListInMapValue.new, SpecNamespace::NestedListInMapValue.new
+ [a, b].each do |thrift_struct|
+ thrift_struct.value = { 1 => [1, 2, 3], 2 => [2, 3, 4] }
+ thrift_struct.validate
+ end
+ expect(a).to eq(b)
+ b.value[3] = [3, 4, 5]
+ expect(a).not_to eq(b)
+ end
+ end
+
+ it "should support nested sets inside lists" do
+ with_type_checking do
+ a, b = SpecNamespace::NestedSetInList.new, SpecNamespace::NestedSetInList.new
+ [a, b].each do |thrift_struct|
+ thrift_struct.value = [ [1, 2, 3].to_set, [2, 3, 4].to_set ]
+ thrift_struct.validate
+ end
+ expect(a).to eq(b)
+ b.value.push([3, 4, 5].to_set)
+ expect(a).not_to eq(b)
+ end
+ end
+
+ it "should support nested sets inside sets" do
+ with_type_checking do
+ a, b = SpecNamespace::NestedSetInSet.new, SpecNamespace::NestedSetInSet.new
+ [a, b].each do |thrift_struct|
+ thrift_struct.value = [ [1, 2, 3].to_set, [2, 3, 4].to_set ].to_set
+ thrift_struct.validate
+ end
+ expect(a).to eq(b)
+ b.value.add([3, 4, 5].to_set)
+ expect(a).not_to eq(b)
+ end
+ end
+
+ it "should support nested sets in map keys" do
+ with_type_checking do
+ a, b = SpecNamespace::NestedSetInMapKey.new, SpecNamespace::NestedSetInMapKey.new
+ [a, b].each do |thrift_struct|
+ thrift_struct.value = { [1, 2, 3].to_set => 1, [2, 3, 4].to_set => 2 }
+ thrift_struct.validate
+ end
+ expect(a).to eq(b)
+ b.value[[3, 4, 5].to_set] = 3
+ expect(a).not_to eq(b)
+ end
+ end
+
+ it "should support nested sets in map values" do
+ with_type_checking do
+ a, b = SpecNamespace::NestedSetInMapValue.new, SpecNamespace::NestedSetInMapValue.new
+ [a, b].each do |thrift_struct|
+ thrift_struct.value = { 1 => [1, 2, 3].to_set, 2 => [2, 3, 4].to_set }
+ thrift_struct.validate
+ end
+ expect(a).to eq(b)
+ b.value[3] = [3, 4, 5].to_set
+ expect(a).not_to eq(b)
+ end
+ end
+
+ it "should support nested maps inside lists" do
+ with_type_checking do
+ a, b = SpecNamespace::NestedMapInList.new, SpecNamespace::NestedMapInList.new
+ [a, b].each do |thrift_struct|
+ thrift_struct.value = [ {1 => 2, 3 => 4}, {2 => 3, 4 => 5} ]
+ thrift_struct.validate
+ end
+ expect(a).to eq(b)
+ b.value.push({ 3 => 4, 5 => 6 })
+ expect(a).not_to eq(b)
+ end
+ end
+
+ it "should support nested maps inside sets" do
+ with_type_checking do
+ a, b = SpecNamespace::NestedMapInSet.new, SpecNamespace::NestedMapInSet.new
+ [a, b].each do |thrift_struct|
+ thrift_struct.value = [ {1 => 2, 3 => 4}, {2 => 3, 4 => 5} ].to_set
+ thrift_struct.validate
+ end
+ expect(a).to eq(b)
+ b.value.add({ 3 => 4, 5 => 6 })
+ expect(a).not_to eq(b)
+ end
+ end
+
+ it "should support nested maps in map keys" do
+ with_type_checking do
+ a, b = SpecNamespace::NestedMapInMapKey.new, SpecNamespace::NestedMapInMapKey.new
+ [a, b].each do |thrift_struct|
+ thrift_struct.value = { { 1 => 2, 3 => 4} => 1, {2 => 3, 4 => 5} => 2 }
+ thrift_struct.validate
+ end
+ expect(a).to eq(b)
+ b.value[{3 => 4, 5 => 6}] = 3
+ expect(a).not_to eq(b)
+ end
+ end
+
+ it "should support nested maps in map values" do
+ with_type_checking do
+ a, b = SpecNamespace::NestedMapInMapValue.new, SpecNamespace::NestedMapInMapValue.new
+ [a, b].each do |thrift_struct|
+ thrift_struct.value = { 1 => { 1 => 2, 3 => 4}, 2 => {2 => 3, 4 => 5} }
+ thrift_struct.validate
+ end
+ expect(a).to eq(b)
+ b.value[3] = { 3 => 4, 5 => 6 }
+ expect(a).not_to eq(b)
+ end
+ end
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/spec/struct_spec.rb b/src/jaegertracing/thrift/lib/rb/spec/struct_spec.rb
new file mode 100644
index 000000000..bbd502b62
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/spec/struct_spec.rb
@@ -0,0 +1,293 @@
+#
+# 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 'Struct' do
+
+ describe Thrift::Struct do
+ it "should iterate over all fields properly" do
+ fields = {}
+ SpecNamespace::Foo.new.each_field { |fid,field_info| fields[fid] = field_info }
+ expect(fields).to eq(SpecNamespace::Foo::FIELDS)
+ end
+
+ it "should initialize all fields to defaults" do
+ validate_default_arguments(SpecNamespace::Foo.new)
+ end
+
+ it "should initialize all fields to defaults and accept a block argument" do
+ SpecNamespace::Foo.new do |f|
+ validate_default_arguments(f)
+ end
+ end
+
+ def validate_default_arguments(object)
+ expect(object.simple).to eq(53)
+ expect(object.words).to eq("words")
+ expect(object.hello).to eq(SpecNamespace::Hello.new(:greeting => 'hello, world!'))
+ expect(object.ints).to eq([1, 2, 2, 3])
+ expect(object.complex).to be_nil
+ expect(object.shorts).to eq(Set.new([5, 17, 239]))
+ end
+
+ it "should not share default values between instances" do
+ begin
+ struct = SpecNamespace::Foo.new
+ struct.ints << 17
+ expect(SpecNamespace::Foo.new.ints).to eq([1,2,2,3])
+ ensure
+ # ensure no leakage to other tests
+ SpecNamespace::Foo::FIELDS[4][:default] = [1,2,2,3]
+ end
+ end
+
+ it "should properly initialize boolean values" do
+ struct = SpecNamespace::BoolStruct.new(:yesno => false)
+ expect(struct.yesno).to be_falsey
+ end
+
+ it "should have proper == semantics" do
+ expect(SpecNamespace::Foo.new).not_to eq(SpecNamespace::Hello.new)
+ expect(SpecNamespace::Foo.new).to eq(SpecNamespace::Foo.new)
+ expect(SpecNamespace::Foo.new(:simple => 52)).not_to eq(SpecNamespace::Foo.new)
+ end
+
+ it "should print enum value names in inspect" do
+ expect(SpecNamespace::StructWithSomeEnum.new(:some_enum => SpecNamespace::SomeEnum::ONE).inspect).to eq("<SpecNamespace::StructWithSomeEnum some_enum:ONE (0)>")
+
+ expect(SpecNamespace::StructWithEnumMap.new(:my_map => {SpecNamespace::SomeEnum::ONE => [SpecNamespace::SomeEnum::TWO]}).inspect).to eq("<SpecNamespace::StructWithEnumMap my_map:{ONE (0): [TWO (1)]}>")
+ end
+
+ it "should pretty print binary fields" do
+ expect(SpecNamespace::Foo2.new(:my_binary => "\001\002\003").inspect).to eq("<SpecNamespace::Foo2 my_binary:010203>")
+ end
+
+ it "should offer field? methods" do
+ expect(SpecNamespace::Foo.new.opt_string?).to be_falsey
+ expect(SpecNamespace::Foo.new(:simple => 52).simple?).to be_truthy
+ expect(SpecNamespace::Foo.new(:my_bool => false).my_bool?).to be_truthy
+ expect(SpecNamespace::Foo.new(:my_bool => true).my_bool?).to be_truthy
+ end
+
+ it "should be comparable" do
+ s1 = SpecNamespace::StructWithSomeEnum.new(:some_enum => SpecNamespace::SomeEnum::ONE)
+ s2 = SpecNamespace::StructWithSomeEnum.new(:some_enum => SpecNamespace::SomeEnum::TWO)
+
+ expect(s1 <=> s2).to eq(-1)
+ expect(s2 <=> s1).to eq(1)
+ expect(s1 <=> s1).to eq(0)
+ expect(s1 <=> SpecNamespace::StructWithSomeEnum.new()).to eq(-1)
+ end
+
+ it "should read itself off the wire" do
+ struct = SpecNamespace::Foo.new
+ prot = Thrift::BaseProtocol.new(double("transport"))
+ expect(prot).to receive(:read_struct_begin).twice
+ expect(prot).to receive(:read_struct_end).twice
+ expect(prot).to receive(:read_field_begin).and_return(
+ ['complex', Thrift::Types::MAP, 5], # Foo
+ ['words', Thrift::Types::STRING, 2], # Foo
+ ['hello', Thrift::Types::STRUCT, 3], # Foo
+ ['greeting', Thrift::Types::STRING, 1], # Hello
+ [nil, Thrift::Types::STOP, 0], # Hello
+ ['simple', Thrift::Types::I32, 1], # Foo
+ ['ints', Thrift::Types::LIST, 4], # Foo
+ ['shorts', Thrift::Types::SET, 6], # Foo
+ [nil, Thrift::Types::STOP, 0] # Hello
+ )
+ expect(prot).to receive(:read_field_end).exactly(7).times
+ expect(prot).to receive(:read_map_begin).and_return(
+ [Thrift::Types::I32, Thrift::Types::MAP, 2], # complex
+ [Thrift::Types::STRING, Thrift::Types::DOUBLE, 2], # complex/1/value
+ [Thrift::Types::STRING, Thrift::Types::DOUBLE, 1] # complex/2/value
+ )
+ expect(prot).to receive(:read_map_end).exactly(3).times
+ expect(prot).to receive(:read_list_begin).and_return([Thrift::Types::I32, 4])
+ expect(prot).to receive(:read_list_end)
+ expect(prot).to receive(:read_set_begin).and_return([Thrift::Types::I16, 2])
+ expect(prot).to receive(:read_set_end)
+ expect(prot).to receive(:read_i32).and_return(
+ 1, 14, # complex keys
+ 42, # simple
+ 4, 23, 4, 29 # ints
+ )
+ expect(prot).to receive(:read_string).and_return("pi", "e", "feigenbaum", "apple banana", "what's up?")
+ expect(prot).to receive(:read_double).and_return(Math::PI, Math::E, 4.669201609)
+ expect(prot).to receive(:read_i16).and_return(2, 3)
+ expect(prot).not_to receive(:skip)
+ struct.read(prot)
+
+ expect(struct.simple).to eq(42)
+ expect(struct.complex).to eq({1 => {"pi" => Math::PI, "e" => Math::E}, 14 => {"feigenbaum" => 4.669201609}})
+ expect(struct.hello).to eq(SpecNamespace::Hello.new(:greeting => "what's up?"))
+ expect(struct.words).to eq("apple banana")
+ expect(struct.ints).to eq([4, 23, 4, 29])
+ expect(struct.shorts).to eq(Set.new([3, 2]))
+ end
+
+ it "should serialize false boolean fields correctly" do
+ b = SpecNamespace::BoolStruct.new(:yesno => false)
+ prot = Thrift::BinaryProtocol.new(Thrift::MemoryBufferTransport.new)
+ expect(prot).to receive(:write_bool).with(false)
+ b.write(prot)
+ end
+
+ it "should skip unexpected fields in structs and use default values" do
+ struct = SpecNamespace::Foo.new
+ prot = Thrift::BaseProtocol.new(double("transport"))
+ expect(prot).to receive(:read_struct_begin)
+ expect(prot).to receive(:read_struct_end)
+ expect(prot).to receive(:read_field_begin).and_return(
+ ['simple', Thrift::Types::I32, 1],
+ ['complex', Thrift::Types::STRUCT, 5],
+ ['thinz', Thrift::Types::MAP, 7],
+ ['foobar', Thrift::Types::I32, 3],
+ ['words', Thrift::Types::STRING, 2],
+ [nil, Thrift::Types::STOP, 0]
+ )
+ expect(prot).to receive(:read_field_end).exactly(5).times
+ expect(prot).to receive(:read_i32).and_return(42)
+ expect(prot).to receive(:read_string).and_return("foobar")
+ expect(prot).to receive(:skip).with(Thrift::Types::STRUCT)
+ expect(prot).to receive(:skip).with(Thrift::Types::MAP)
+ # prot.should_receive(:read_map_begin).and_return([Thrift::Types::I32, Thrift::Types::I32, 0])
+ # prot.should_receive(:read_map_end)
+ expect(prot).to receive(:skip).with(Thrift::Types::I32)
+ struct.read(prot)
+
+ expect(struct.simple).to eq(42)
+ expect(struct.complex).to be_nil
+ expect(struct.words).to eq("foobar")
+ expect(struct.hello).to eq(SpecNamespace::Hello.new(:greeting => 'hello, world!'))
+ expect(struct.ints).to eq([1, 2, 2, 3])
+ expect(struct.shorts).to eq(Set.new([5, 17, 239]))
+ end
+
+ it "should write itself to the wire" do
+ prot = Thrift::BaseProtocol.new(double("transport")) #mock("Protocol")
+ expect(prot).to receive(:write_struct_begin).with("SpecNamespace::Foo")
+ expect(prot).to receive(:write_struct_begin).with("SpecNamespace::Hello")
+ expect(prot).to receive(:write_struct_end).twice
+ expect(prot).to receive(:write_field_begin).with('ints', Thrift::Types::LIST, 4)
+ expect(prot).to receive(:write_i32).with(1)
+ expect(prot).to receive(:write_i32).with(2).twice
+ expect(prot).to receive(:write_i32).with(3)
+ expect(prot).to receive(:write_field_begin).with('complex', Thrift::Types::MAP, 5)
+ expect(prot).to receive(:write_i32).with(5)
+ expect(prot).to receive(:write_string).with('foo')
+ expect(prot).to receive(:write_double).with(1.23)
+ expect(prot).to receive(:write_field_begin).with('shorts', Thrift::Types::SET, 6)
+ expect(prot).to receive(:write_i16).with(5)
+ expect(prot).to receive(:write_i16).with(17)
+ expect(prot).to receive(:write_i16).with(239)
+ expect(prot).to receive(:write_field_stop).twice
+ expect(prot).to receive(:write_field_end).exactly(6).times
+ expect(prot).to receive(:write_field_begin).with('simple', Thrift::Types::I32, 1)
+ expect(prot).to receive(:write_i32).with(53)
+ expect(prot).to receive(:write_field_begin).with('hello', Thrift::Types::STRUCT, 3)
+ expect(prot).to receive(:write_field_begin).with('greeting', Thrift::Types::STRING, 1)
+ expect(prot).to receive(:write_string).with('hello, world!')
+ expect(prot).to receive(:write_map_begin).with(Thrift::Types::I32, Thrift::Types::MAP, 1)
+ expect(prot).to receive(:write_map_begin).with(Thrift::Types::STRING, Thrift::Types::DOUBLE, 1)
+ expect(prot).to receive(:write_map_end).twice
+ expect(prot).to receive(:write_list_begin).with(Thrift::Types::I32, 4)
+ expect(prot).to receive(:write_list_end)
+ expect(prot).to receive(:write_set_begin).with(Thrift::Types::I16, 3)
+ expect(prot).to receive(:write_set_end)
+
+ struct = SpecNamespace::Foo.new
+ struct.words = nil
+ struct.complex = {5 => {"foo" => 1.23}}
+ struct.write(prot)
+ end
+
+ it "should raise an exception if presented with an unknown container" do
+ # yeah this is silly, but I'm going for code coverage here
+ struct = SpecNamespace::Foo.new
+ expect { struct.send :write_container, nil, nil, {:type => "foo"} }.to raise_error(StandardError, "Not a container type: foo")
+ end
+
+ it "should support optional type-checking in Thrift::Struct.new" do
+ Thrift.type_checking = true
+ begin
+ expect { SpecNamespace::Hello.new(:greeting => 3) }.to raise_error(Thrift::TypeError, /Expected Types::STRING, received (Integer|Fixnum) for field greeting/)
+ ensure
+ Thrift.type_checking = false
+ end
+ expect { SpecNamespace::Hello.new(:greeting => 3) }.not_to raise_error
+ end
+
+ it "should support optional type-checking in field accessors" do
+ Thrift.type_checking = true
+ begin
+ hello = SpecNamespace::Hello.new
+ expect { hello.greeting = 3 }.to raise_error(Thrift::TypeError, /Expected Types::STRING, received (Integer|Fixnum) for field greeting/)
+ ensure
+ Thrift.type_checking = false
+ end
+ expect { hello.greeting = 3 }.not_to raise_error
+ end
+
+ it "should raise an exception when unknown types are given to Thrift::Struct.new" do
+ expect { SpecNamespace::Hello.new(:fish => 'salmon') }.to raise_error(Exception, "Unknown key given to SpecNamespace::Hello.new: fish")
+ end
+
+ it "should support `raise Xception, 'message'` for Exception structs" do
+ begin
+ raise SpecNamespace::Xception, "something happened"
+ rescue Thrift::Exception => e
+ expect(e.message).to eq("something happened")
+ expect(e.code).to eq(1)
+ # ensure it gets serialized properly, this is the really important part
+ prot = Thrift::BaseProtocol.new(double("trans"))
+ expect(prot).to receive(:write_struct_begin).with("SpecNamespace::Xception")
+ expect(prot).to receive(:write_struct_end)
+ expect(prot).to receive(:write_field_begin).with('message', Thrift::Types::STRING, 1)#, "something happened")
+ expect(prot).to receive(:write_string).with("something happened")
+ expect(prot).to receive(:write_field_begin).with('code', Thrift::Types::I32, 2)#, 1)
+ expect(prot).to receive(:write_i32).with(1)
+ expect(prot).to receive(:write_field_stop)
+ expect(prot).to receive(:write_field_end).twice
+
+ e.write(prot)
+ end
+ end
+
+ it "should support the regular initializer for exception structs" do
+ begin
+ raise SpecNamespace::Xception, :message => "something happened", :code => 5
+ rescue Thrift::Exception => e
+ expect(e.message).to eq("something happened")
+ expect(e.code).to eq(5)
+ prot = Thrift::BaseProtocol.new(double("trans"))
+ expect(prot).to receive(:write_struct_begin).with("SpecNamespace::Xception")
+ expect(prot).to receive(:write_struct_end)
+ expect(prot).to receive(:write_field_begin).with('message', Thrift::Types::STRING, 1)
+ expect(prot).to receive(:write_string).with("something happened")
+ expect(prot).to receive(:write_field_begin).with('code', Thrift::Types::I32, 2)
+ expect(prot).to receive(:write_i32).with(5)
+ expect(prot).to receive(:write_field_stop)
+ expect(prot).to receive(:write_field_end).twice
+
+ e.write(prot)
+ end
+ end
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/spec/thin_http_server_spec.rb b/src/jaegertracing/thrift/lib/rb/spec/thin_http_server_spec.rb
new file mode 100644
index 000000000..665391b7d
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/spec/thin_http_server_spec.rb
@@ -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'
+require 'rack/test'
+require 'thrift/server/thin_http_server'
+
+describe Thrift::ThinHTTPServer do
+
+ let(:processor) { double('processor') }
+
+ describe "#initialize" do
+
+ context "when using the defaults" do
+
+ it "binds to port 80, with host 0.0.0.0, a path of '/'" do
+ expect(Thin::Server).to receive(:new).with('0.0.0.0', 80, an_instance_of(Rack::Builder))
+ Thrift::ThinHTTPServer.new(processor)
+ end
+
+ it 'creates a ThinHTTPServer::RackApplicationContext' do
+ expect(Thrift::ThinHTTPServer::RackApplication).to receive(:for).with("/", processor, an_instance_of(Thrift::BinaryProtocolFactory)).and_return(anything)
+ Thrift::ThinHTTPServer.new(processor)
+ end
+
+ it "uses the BinaryProtocolFactory" do
+ expect(Thrift::BinaryProtocolFactory).to receive(:new)
+ Thrift::ThinHTTPServer.new(processor)
+ end
+
+ end
+
+ context "when using the options" do
+
+ it 'accepts :ip, :port, :path' do
+ ip = "192.168.0.1"
+ port = 3000
+ path = "/thin"
+ expect(Thin::Server).to receive(:new).with(ip, port, an_instance_of(Rack::Builder))
+ Thrift::ThinHTTPServer.new(processor,
+ :ip => ip,
+ :port => port,
+ :path => path)
+ end
+
+ it 'creates a ThinHTTPServer::RackApplicationContext with a different protocol factory' do
+ expect(Thrift::ThinHTTPServer::RackApplication).to receive(:for).with("/", processor, an_instance_of(Thrift::JsonProtocolFactory)).and_return(anything)
+ Thrift::ThinHTTPServer.new(processor,
+ :protocol_factory => Thrift::JsonProtocolFactory.new)
+ end
+
+ end
+
+ end
+
+ describe "#serve" do
+
+ it 'starts the Thin server' do
+ underlying_thin_server = double('thin server', :start => true)
+ allow(Thin::Server).to receive(:new).and_return(underlying_thin_server)
+
+ thin_thrift_server = Thrift::ThinHTTPServer.new(processor)
+
+ expect(underlying_thin_server).to receive(:start)
+ thin_thrift_server.serve
+ end
+ end
+
+end
+
+describe Thrift::ThinHTTPServer::RackApplication do
+ include Rack::Test::Methods
+
+ let(:processor) { double('processor') }
+ let(:protocol_factory) { double('protocol factory') }
+
+ def app
+ Thrift::ThinHTTPServer::RackApplication.for("/", processor, protocol_factory)
+ end
+
+ context "404 response" do
+
+ it 'receives a non-POST' do
+ header('Content-Type', "application/x-thrift")
+ get "/"
+ expect(last_response.status).to be 404
+ end
+
+ it 'receives a header other than application/x-thrift' do
+ header('Content-Type', "application/json")
+ post "/"
+ expect(last_response.status).to be 404
+ end
+
+ end
+
+ context "200 response" do
+
+ before do
+ allow(protocol_factory).to receive(:get_protocol)
+ allow(processor).to receive(:process)
+ end
+
+ it 'creates an IOStreamTransport' do
+ header('Content-Type', "application/x-thrift")
+ expect(Thrift::IOStreamTransport).to receive(:new).with(an_instance_of(Rack::Lint::InputWrapper), an_instance_of(Rack::Response))
+ post "/"
+ end
+
+ it 'fetches the right protocol based on the Transport' do
+ header('Content-Type', "application/x-thrift")
+ expect(protocol_factory).to receive(:get_protocol).with(an_instance_of(Thrift::IOStreamTransport))
+ post "/"
+ end
+
+ it 'status code 200' do
+ header('Content-Type', "application/x-thrift")
+ post "/"
+ expect(last_response.ok?).to be_truthy
+ end
+
+ end
+
+end
+
diff --git a/src/jaegertracing/thrift/lib/rb/spec/types_spec.rb b/src/jaegertracing/thrift/lib/rb/spec/types_spec.rb
new file mode 100644
index 000000000..d595ab563
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/spec/types_spec.rb
@@ -0,0 +1,118 @@
+#
+# 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::Types do
+
+ before(:each) do
+ Thrift.type_checking = true
+ end
+
+ after(:each) do
+ Thrift.type_checking = false
+ end
+
+ context 'type checking' do
+ it "should return the proper name for each type" do
+ expect(Thrift.type_name(Thrift::Types::I16)).to eq("Types::I16")
+ expect(Thrift.type_name(Thrift::Types::VOID)).to eq("Types::VOID")
+ expect(Thrift.type_name(Thrift::Types::LIST)).to eq("Types::LIST")
+ expect(Thrift.type_name(42)).to be_nil
+ end
+
+ it "should check types properly" do
+ # lambda { Thrift.check_type(nil, Thrift::Types::STOP) }.should raise_error(Thrift::TypeError)
+ expect { Thrift.check_type(3, {:type => Thrift::Types::STOP}, :foo) }.to raise_error(Thrift::TypeError)
+ expect { Thrift.check_type(nil, {:type => Thrift::Types::VOID}, :foo) }.not_to raise_error
+ expect { Thrift.check_type(3, {:type => Thrift::Types::VOID}, :foo) }.to raise_error(Thrift::TypeError)
+ expect { Thrift.check_type(true, {:type => Thrift::Types::BOOL}, :foo) }.not_to raise_error
+ expect { Thrift.check_type(3, {:type => Thrift::Types::BOOL}, :foo) }.to raise_error(Thrift::TypeError)
+ expect { Thrift.check_type(42, {:type => Thrift::Types::BYTE}, :foo) }.not_to raise_error
+ expect { Thrift.check_type(42, {:type => Thrift::Types::I16}, :foo) }.not_to raise_error
+ expect { Thrift.check_type(42, {:type => Thrift::Types::I32}, :foo) }.not_to raise_error
+ expect { Thrift.check_type(42, {:type => Thrift::Types::I64}, :foo) }.not_to raise_error
+ expect { Thrift.check_type(3.14, {:type => Thrift::Types::I32}, :foo) }.to raise_error(Thrift::TypeError)
+ expect { Thrift.check_type(3.14, {:type => Thrift::Types::DOUBLE}, :foo) }.not_to raise_error
+ expect { Thrift.check_type(3, {:type => Thrift::Types::DOUBLE}, :foo) }.to raise_error(Thrift::TypeError)
+ expect { Thrift.check_type("3", {:type => Thrift::Types::STRING}, :foo) }.not_to raise_error
+ expect { Thrift.check_type(3, {:type => Thrift::Types::STRING}, :foo) }.to raise_error(Thrift::TypeError)
+ hello = SpecNamespace::Hello.new
+ expect { Thrift.check_type(hello, {:type => Thrift::Types::STRUCT, :class => SpecNamespace::Hello}, :foo) }.not_to raise_error
+ expect { Thrift.check_type("foo", {:type => Thrift::Types::STRUCT}, :foo) }.to raise_error(Thrift::TypeError)
+ field = {:type => Thrift::Types::MAP, :key => {:type => Thrift::Types::I32}, :value => {:type => Thrift::Types::STRING}}
+ expect { Thrift.check_type({1 => "one"}, field, :foo) }.not_to raise_error
+ expect { Thrift.check_type([1], field, :foo) }.to raise_error(Thrift::TypeError)
+ field = {:type => Thrift::Types::LIST, :element => {:type => Thrift::Types::I32}}
+ expect { Thrift.check_type([1], field, :foo) }.not_to raise_error
+ expect { Thrift.check_type({:foo => 1}, field, :foo) }.to raise_error(Thrift::TypeError)
+ field = {:type => Thrift::Types::SET, :element => {:type => Thrift::Types::I32}}
+ expect { Thrift.check_type(Set.new([1,2]), field, :foo) }.not_to raise_error
+ expect { Thrift.check_type([1,2], field, :foo) }.to raise_error(Thrift::TypeError)
+ expect { Thrift.check_type({:foo => true}, field, :foo) }.to raise_error(Thrift::TypeError)
+ end
+
+ it "should error out if nil is passed and skip_types is false" do
+ expect { Thrift.check_type(nil, {:type => Thrift::Types::BOOL}, :foo, false) }.to raise_error(Thrift::TypeError)
+ expect { Thrift.check_type(nil, {:type => Thrift::Types::BYTE}, :foo, false) }.to raise_error(Thrift::TypeError)
+ expect { Thrift.check_type(nil, {:type => Thrift::Types::I16}, :foo, false) }.to raise_error(Thrift::TypeError)
+ expect { Thrift.check_type(nil, {:type => Thrift::Types::I32}, :foo, false) }.to raise_error(Thrift::TypeError)
+ expect { Thrift.check_type(nil, {:type => Thrift::Types::I64}, :foo, false) }.to raise_error(Thrift::TypeError)
+ expect { Thrift.check_type(nil, {:type => Thrift::Types::DOUBLE}, :foo, false) }.to raise_error(Thrift::TypeError)
+ expect { Thrift.check_type(nil, {:type => Thrift::Types::STRING}, :foo, false) }.to raise_error(Thrift::TypeError)
+ expect { Thrift.check_type(nil, {:type => Thrift::Types::STRUCT}, :foo, false) }.to raise_error(Thrift::TypeError)
+ expect { Thrift.check_type(nil, {:type => Thrift::Types::LIST}, :foo, false) }.to raise_error(Thrift::TypeError)
+ expect { Thrift.check_type(nil, {:type => Thrift::Types::SET}, :foo, false) }.to raise_error(Thrift::TypeError)
+ expect { Thrift.check_type(nil, {:type => Thrift::Types::MAP}, :foo, false) }.to raise_error(Thrift::TypeError)
+ end
+
+ it "should check element types on containers" do
+ field = {:type => Thrift::Types::LIST, :element => {:type => Thrift::Types::I32}}
+ expect { Thrift.check_type([1, 2], field, :foo) }.not_to raise_error
+ expect { Thrift.check_type([1, nil, 2], field, :foo) }.to raise_error(Thrift::TypeError)
+ field = {:type => Thrift::Types::MAP, :key => {:type => Thrift::Types::I32}, :value => {:type => Thrift::Types::STRING}}
+ expect { Thrift.check_type({1 => "one", 2 => "two"}, field, :foo) }.not_to raise_error
+ expect { Thrift.check_type({1 => "one", nil => "nil"}, field, :foo) }.to raise_error(Thrift::TypeError)
+ expect { Thrift.check_type({1 => nil, 2 => "two"}, field, :foo) }.to raise_error(Thrift::TypeError)
+ field = {:type => Thrift::Types::SET, :element => {:type => Thrift::Types::I32}}
+ expect { Thrift.check_type(Set.new([1, 2]), field, :foo) }.not_to raise_error
+ expect { Thrift.check_type(Set.new([1, nil, 2]), field, :foo) }.to raise_error(Thrift::TypeError)
+ expect { Thrift.check_type(Set.new([1, 2.3, 2]), field, :foo) }.to raise_error(Thrift::TypeError)
+
+ field = {:type => Thrift::Types::STRUCT, :class => SpecNamespace::Hello}
+ expect { Thrift.check_type(SpecNamespace::BoolStruct, field, :foo) }.to raise_error(Thrift::TypeError)
+ end
+
+ it "should give the Thrift::TypeError a readable message" do
+ msg = /Expected Types::STRING, received (Integer|Fixnum) for field foo/
+ expect { Thrift.check_type(3, {:type => Thrift::Types::STRING}, :foo) }.to raise_error(Thrift::TypeError, msg)
+ msg = /Expected Types::STRING, received (Integer|Fixnum) for field foo.element/
+ field = {:type => Thrift::Types::LIST, :element => {:type => Thrift::Types::STRING}}
+ expect { Thrift.check_type([3], field, :foo) }.to raise_error(Thrift::TypeError, msg)
+ msg = "Expected Types::I32, received NilClass for field foo.element.key"
+ field = {:type => Thrift::Types::LIST,
+ :element => {:type => Thrift::Types::MAP,
+ :key => {:type => Thrift::Types::I32},
+ :value => {:type => Thrift::Types::I32}}}
+ expect { Thrift.check_type([{nil => 3}], field, :foo) }.to raise_error(Thrift::TypeError, msg)
+ msg = "Expected Types::I32, received NilClass for field foo.element.value"
+ expect { Thrift.check_type([{1 => nil}], field, :foo) }.to raise_error(Thrift::TypeError, msg)
+ end
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/spec/union_spec.rb b/src/jaegertracing/thrift/lib/rb/spec/union_spec.rb
new file mode 100644
index 000000000..efb385346
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/spec/union_spec.rb
@@ -0,0 +1,214 @@
+#
+# 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 'Union' do
+
+ describe Thrift::Union do
+ it "should return nil value in unset union" do
+ union = SpecNamespace::My_union.new
+ expect(union.get_set_field).to eq(nil)
+ expect(union.get_value).to eq(nil)
+ end
+
+ it "should set a field and be accessible through get_value and the named field accessor" do
+ union = SpecNamespace::My_union.new
+ union.integer32 = 25
+ expect(union.get_set_field).to eq(:integer32)
+ expect(union.get_value).to eq(25)
+ expect(union.integer32).to eq(25)
+ end
+
+ it "should work correctly when instantiated with static field constructors" do
+ union = SpecNamespace::My_union.integer32(5)
+ expect(union.get_set_field).to eq(:integer32)
+ expect(union.integer32).to eq(5)
+ end
+
+ it "should raise for wrong set field" do
+ union = SpecNamespace::My_union.new
+ union.integer32 = 25
+ expect { union.some_characters }.to raise_error(RuntimeError, "some_characters is not union's set field.")
+ end
+
+ it "should raise for wrong set field when hash initialized and type checking is off" do
+ Thrift.type_checking = false
+ union = SpecNamespace::My_union.new({incorrect_field: :incorrect})
+ expect { Thrift::Serializer.new.serialize(union) }.to raise_error(RuntimeError, "set_field is not valid for this union!")
+ end
+
+ it "should not be equal to nil" do
+ union = SpecNamespace::My_union.new
+ expect(union).not_to eq(nil)
+ end
+
+ it "should not be equal with an empty String" do
+ union = SpecNamespace::My_union.new
+ expect(union).not_to eq('')
+ end
+
+ it "should not equate two different unions, i32 vs. string" do
+ union = SpecNamespace::My_union.new(:integer32, 25)
+ other_union = SpecNamespace::My_union.new(:some_characters, "blah!")
+ expect(union).not_to eq(other_union)
+ end
+
+ it "should properly reset setfield and setvalue" do
+ union = SpecNamespace::My_union.new(:integer32, 25)
+ expect(union.get_set_field).to eq(:integer32)
+ union.some_characters = "blah!"
+ expect(union.get_set_field).to eq(:some_characters)
+ expect(union.get_value).to eq("blah!")
+ expect { union.integer32 }.to raise_error(RuntimeError, "integer32 is not union's set field.")
+ end
+
+ it "should not equate two different unions with different values" do
+ union = SpecNamespace::My_union.new(:integer32, 25)
+ other_union = SpecNamespace::My_union.new(:integer32, 400)
+ expect(union).not_to eq(other_union)
+ end
+
+ it "should not equate two different unions with different fields" do
+ union = SpecNamespace::My_union.new(:integer32, 25)
+ other_union = SpecNamespace::My_union.new(:other_i32, 25)
+ expect(union).not_to eq(other_union)
+ end
+
+ it "should inspect properly" do
+ union = SpecNamespace::My_union.new(:integer32, 25)
+ expect(union.inspect).to eq("<SpecNamespace::My_union integer32: 25>")
+ end
+
+ it "should not allow setting with instance_variable_set" do
+ union = SpecNamespace::My_union.new(:integer32, 27)
+ union.instance_variable_set(:@some_characters, "hallo!")
+ expect(union.get_set_field).to eq(:integer32)
+ expect(union.get_value).to eq(27)
+ expect { union.some_characters }.to raise_error(RuntimeError, "some_characters is not union's set field.")
+ end
+
+ it "should serialize to binary correctly" do
+ trans = Thrift::MemoryBufferTransport.new
+ proto = Thrift::BinaryProtocol.new(trans)
+
+ union = SpecNamespace::My_union.new(:integer32, 25)
+ union.write(proto)
+
+ other_union = SpecNamespace::My_union.new(:integer32, 25)
+ other_union.read(proto)
+ expect(other_union).to eq(union)
+ end
+
+ it "should serialize to json correctly" do
+ trans = Thrift::MemoryBufferTransport.new
+ proto = Thrift::JsonProtocol.new(trans)
+
+ union = SpecNamespace::My_union.new(:integer32, 25)
+ union.write(proto)
+
+ other_union = SpecNamespace::My_union.new(:integer32, 25)
+ other_union.read(proto)
+ expect(other_union).to eq(union)
+ end
+
+ it "should raise when validating unset union" do
+ union = SpecNamespace::My_union.new
+ expect { union.validate }.to raise_error(StandardError, "Union fields are not set.")
+
+ other_union = SpecNamespace::My_union.new(:integer32, 1)
+ expect { other_union.validate }.not_to raise_error
+ end
+
+ it "should validate an enum field properly" do
+ union = SpecNamespace::TestUnion.new(:enum_field, 3)
+ expect(union.get_set_field).to eq(:enum_field)
+ expect { union.validate }.to raise_error(Thrift::ProtocolException, "Invalid value of field enum_field!")
+
+ other_union = SpecNamespace::TestUnion.new(:enum_field, 1)
+ expect { other_union.validate }.not_to raise_error
+ end
+
+ it "should properly serialize and match structs with a union" do
+ union = SpecNamespace::My_union.new(:integer32, 26)
+ swu = SpecNamespace::Struct_with_union.new(:fun_union => union)
+
+ trans = Thrift::MemoryBufferTransport.new
+ proto = Thrift::CompactProtocol.new(trans)
+
+ swu.write(proto)
+
+ other_union = SpecNamespace::My_union.new(:some_characters, "hello there")
+ swu2 = SpecNamespace::Struct_with_union.new(:fun_union => other_union)
+
+ expect(swu2).not_to eq(swu)
+
+ swu2.read(proto)
+ expect(swu2).to eq(swu)
+ end
+
+ it "should support old style constructor" do
+ union = SpecNamespace::My_union.new(:integer32 => 26)
+ expect(union.get_set_field).to eq(:integer32)
+ expect(union.get_value).to eq(26)
+ end
+
+ it "should not throw an error when inspected and unset" do
+ expect{SpecNamespace::TestUnion.new().inspect}.not_to raise_error
+ end
+
+ it "should print enum value name when inspected" do
+ expect(SpecNamespace::My_union.new(:some_enum => SpecNamespace::SomeEnum::ONE).inspect).to eq("<SpecNamespace::My_union some_enum: ONE (0)>")
+
+ expect(SpecNamespace::My_union.new(:my_map => {SpecNamespace::SomeEnum::ONE => [SpecNamespace::SomeEnum::TWO]}).inspect).to eq("<SpecNamespace::My_union my_map: {ONE (0): [TWO (1)]}>")
+ end
+
+ it "should offer field? methods" do
+ expect(SpecNamespace::My_union.new.some_enum?).to be_falsey
+ expect(SpecNamespace::My_union.new(:some_enum => SpecNamespace::SomeEnum::ONE).some_enum?).to be_truthy
+ expect(SpecNamespace::My_union.new(:im_true => false).im_true?).to be_truthy
+ expect(SpecNamespace::My_union.new(:im_true => true).im_true?).to be_truthy
+ end
+
+ it "should pretty print binary fields" do
+ expect(SpecNamespace::TestUnion.new(:binary_field => "\001\002\003").inspect).to eq("<SpecNamespace::TestUnion binary_field: 010203>")
+ end
+
+ it "should be comparable" do
+ relationships = [
+ [0, -1, -1, -1],
+ [1, 0, -1, -1],
+ [1, 1, 0, -1],
+ [1, 1, 1, 0]]
+
+ objs = [
+ SpecNamespace::TestUnion.new(:string_field, "blah"),
+ SpecNamespace::TestUnion.new(:string_field, "blahblah"),
+ SpecNamespace::TestUnion.new(:i32_field, 1),
+ SpecNamespace::TestUnion.new()]
+
+ for y in 0..3
+ for x in 0..3
+ # puts "#{objs[y].inspect} <=> #{objs[x].inspect} should == #{relationships[y][x]}"
+ expect(objs[y] <=> objs[x]).to eq(relationships[y][x])
+ end
+ end
+ end
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/spec/unix_socket_spec.rb b/src/jaegertracing/thrift/lib/rb/spec/unix_socket_spec.rb
new file mode 100644
index 000000000..8623e95a0
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/spec/unix_socket_spec.rb
@@ -0,0 +1,116 @@
+#
+# 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__)}/socket_spec_shared")
+
+describe 'UNIXSocket' do
+
+ describe Thrift::UNIXSocket do
+ before(:each) do
+ @path = '/tmp/thrift_spec_socket'
+ @socket = Thrift::UNIXSocket.new(@path)
+ @handle = double("Handle", :closed? => false)
+ allow(@handle).to receive(:close)
+ allow(::UNIXSocket).to receive(:new).and_return(@handle)
+ end
+
+ it_should_behave_like "a socket"
+
+ it "should raise a TransportException when it cannot open a socket" do
+ expect(::UNIXSocket).to receive(:new).and_raise(StandardError)
+ expect { @socket.open }.to raise_error(Thrift::TransportException) { |e| expect(e.type).to eq(Thrift::TransportException::NOT_OPEN) }
+ end
+
+ it "should accept an optional timeout" do
+ allow(::UNIXSocket).to receive(:new)
+ expect(Thrift::UNIXSocket.new(@path, 5).timeout).to eq(5)
+ end
+
+ it "should provide a reasonable to_s" do
+ allow(::UNIXSocket).to receive(:new)
+ expect(Thrift::UNIXSocket.new(@path).to_s).to eq("domain(#{@path})")
+ end
+ end
+
+ describe Thrift::UNIXServerSocket do
+ before(:each) do
+ @path = '/tmp/thrift_spec_socket'
+ @socket = Thrift::UNIXServerSocket.new(@path)
+ end
+
+ it "should create a handle when calling listen" do
+ expect(UNIXServer).to receive(:new).with(@path)
+ @socket.listen
+ end
+
+ it "should create a Thrift::UNIXSocket to wrap accepted sockets" do
+ handle = double("UNIXServer")
+ expect(UNIXServer).to receive(:new).with(@path).and_return(handle)
+ @socket.listen
+ sock = double("sock")
+ expect(handle).to receive(:accept).and_return(sock)
+ trans = double("UNIXSocket")
+ expect(Thrift::UNIXSocket).to receive(:new).and_return(trans)
+ expect(trans).to receive(:handle=).with(sock)
+ expect(@socket.accept).to eq(trans)
+ end
+
+ it "should close the handle when closed" do
+ handle = double("UNIXServer", :closed? => false)
+ expect(UNIXServer).to receive(:new).with(@path).and_return(handle)
+ @socket.listen
+ expect(handle).to receive(:close)
+ allow(File).to receive(:delete)
+ @socket.close
+ end
+
+ it "should delete the socket when closed" do
+ handle = double("UNIXServer", :closed? => false)
+ expect(UNIXServer).to receive(:new).with(@path).and_return(handle)
+ @socket.listen
+ allow(handle).to receive(:close)
+ expect(File).to receive(:delete).with(@path)
+ @socket.close
+ end
+
+ it "should return nil when accepting if there is no handle" do
+ expect(@socket.accept).to be_nil
+ end
+
+ it "should return true for closed? when appropriate" do
+ handle = double("UNIXServer", :closed? => false)
+ allow(UNIXServer).to receive(:new).and_return(handle)
+ allow(File).to receive(:delete)
+ @socket.listen
+ expect(@socket).not_to be_closed
+ allow(handle).to receive(:close)
+ @socket.close
+ expect(@socket).to be_closed
+ @socket.listen
+ expect(@socket).not_to be_closed
+ allow(handle).to receive(:closed?).and_return(true)
+ expect(@socket).to be_closed
+ end
+
+ it "should provide a reasonable to_s" do
+ expect(@socket.to_s).to eq("domain(#{@path})")
+ end
+ end
+end
diff --git a/src/jaegertracing/thrift/lib/rb/thrift.gemspec b/src/jaegertracing/thrift/lib/rb/thrift.gemspec
new file mode 100644
index 000000000..869e5d95d
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/rb/thrift.gemspec
@@ -0,0 +1,40 @@
+# -*- encoding: utf-8 -*-
+$:.push File.expand_path("../lib", __FILE__)
+
+Gem::Specification.new do |s|
+ s.name = 'thrift'
+ s.version = '0.13.0'
+ s.authors = ['Apache Thrift Developers']
+ s.email = ['dev@thrift.apache.org']
+ s.homepage = 'http://thrift.apache.org'
+ s.summary = %q{Ruby bindings for Apache Thrift}
+ s.description = %q{Ruby bindings for the Apache Thrift RPC system}
+ s.license = 'Apache-2.0'
+ s.extensions = ['ext/extconf.rb']
+
+ s.has_rdoc = true
+ s.rdoc_options = %w[--line-numbers --inline-source --title Thrift --main README]
+
+ s.rubyforge_project = 'thrift'
+
+ dir = File.expand_path(File.dirname(__FILE__))
+
+ s.files = Dir.glob("{lib,spec}/**/*")
+ s.test_files = Dir.glob("{test,spec,benchmark}/**/*")
+ s.executables = Dir.glob("{bin}/**/*")
+
+ s.extra_rdoc_files = %w[README.md] + Dir.glob("{ext,lib}/**/*.{c,h,rb}")
+
+ s.require_paths = %w[lib ext]
+
+ s.add_development_dependency 'bundler', '~> 1.11'
+ s.add_development_dependency 'pry', '~> 0.11.3'
+ s.add_development_dependency 'pry-byebug', '~> 3.6'
+ s.add_development_dependency 'pry-stack_explorer', '~> 0.4.9.2'
+ s.add_development_dependency 'rack', '~> 2.0'
+ s.add_development_dependency 'rack-test', '~> 0.8.3'
+ s.add_development_dependency 'rake', '~> 12.3'
+ s.add_development_dependency 'rspec', '~> 3.7'
+ s.add_development_dependency 'thin', '~> 1.7'
+end
+