thrift_client-mavericks 0.8.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +15 -0
- data/lib/thrift_client.rb +34 -0
- data/lib/thrift_client/abstract_thrift_client.rb +221 -0
- data/lib/thrift_client/connection.rb +4 -0
- data/lib/thrift_client/connection/base.rb +25 -0
- data/lib/thrift_client/connection/factory.rb +13 -0
- data/lib/thrift_client/connection/http.rb +29 -0
- data/lib/thrift_client/connection/socket.rb +29 -0
- data/lib/thrift_client/event_machine.rb +145 -0
- data/lib/thrift_client/server.rb +108 -0
- data/lib/thrift_client/simple.rb +272 -0
- data/lib/thrift_client/thrift.rb +40 -0
- data/test/greeter/greeter.rb +121 -0
- data/test/greeter/server.rb +44 -0
- data/test/multiple_working_servers_test.rb +112 -0
- data/test/simple_test.rb +136 -0
- data/test/test_helper.rb +12 -0
- data/test/thrift_client_http_test.rb +46 -0
- data/test/thrift_client_test.rb +282 -0
- metadata +112 -0
@@ -0,0 +1,108 @@
|
|
1
|
+
require 'thrift_client/connection'
|
2
|
+
|
3
|
+
module ThriftHelpers
|
4
|
+
class Server
|
5
|
+
class ServerMarkedDown < StandardError; end
|
6
|
+
|
7
|
+
def initialize(connection_string, client_class, options = {})
|
8
|
+
@connection_string = connection_string
|
9
|
+
@client_class = client_class
|
10
|
+
@options = options
|
11
|
+
|
12
|
+
@cached = @options.has_key?(:cached_connections) ? @options[:cached_connections] : true
|
13
|
+
|
14
|
+
@marked_down_til = nil
|
15
|
+
end
|
16
|
+
|
17
|
+
def mark_down!(til)
|
18
|
+
close(true)
|
19
|
+
@marked_down_til = Time.now + til
|
20
|
+
end
|
21
|
+
|
22
|
+
def up?
|
23
|
+
!down?
|
24
|
+
end
|
25
|
+
|
26
|
+
def down?
|
27
|
+
@marked_down_til && @marked_down_til > Time.now
|
28
|
+
end
|
29
|
+
|
30
|
+
def to_s
|
31
|
+
@connection_string
|
32
|
+
end
|
33
|
+
|
34
|
+
def connection
|
35
|
+
@connection ||= Connection::Factory.create(
|
36
|
+
@options[:transport], @options[:transport_wrapper],
|
37
|
+
@connection_string, @options[:connect_timeout])
|
38
|
+
end
|
39
|
+
|
40
|
+
def connect!
|
41
|
+
return if open?
|
42
|
+
|
43
|
+
self.timeout = @options[:connect_timeout]
|
44
|
+
connection.connect!
|
45
|
+
self.timeout = @options[:timeout]
|
46
|
+
end
|
47
|
+
|
48
|
+
def client
|
49
|
+
@client ||= begin
|
50
|
+
connect!
|
51
|
+
|
52
|
+
@client_class.new(
|
53
|
+
@options[:protocol].new(self, *@options[:protocol_extra_params]))
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def open?
|
58
|
+
connection.open?
|
59
|
+
end
|
60
|
+
|
61
|
+
def close(teardown = false)
|
62
|
+
if teardown || !@cached
|
63
|
+
connection.close if open?
|
64
|
+
@client = nil
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def transport
|
69
|
+
connection.transport
|
70
|
+
end
|
71
|
+
|
72
|
+
module TransportInterface
|
73
|
+
def read(sz)
|
74
|
+
transport.read(sz)
|
75
|
+
end
|
76
|
+
|
77
|
+
def read_byte
|
78
|
+
transport.read_byte
|
79
|
+
end
|
80
|
+
|
81
|
+
def read_into_buffer(buffer, size)
|
82
|
+
transport.read_into_buffer(buffer, size)
|
83
|
+
end
|
84
|
+
|
85
|
+
def read_all(sz)
|
86
|
+
transport.read_all(sz)
|
87
|
+
end
|
88
|
+
|
89
|
+
def write(buf)
|
90
|
+
transport.write(buf)
|
91
|
+
end
|
92
|
+
alias_method :<<, :write
|
93
|
+
|
94
|
+
def flush
|
95
|
+
transport.flush
|
96
|
+
end
|
97
|
+
|
98
|
+
def timeout=(timeout)
|
99
|
+
transport.timeout = timeout if transport.respond_to?(:timeout=)
|
100
|
+
end
|
101
|
+
|
102
|
+
def timeout
|
103
|
+
transport.timeout
|
104
|
+
end
|
105
|
+
end
|
106
|
+
include TransportInterface
|
107
|
+
end
|
108
|
+
end
|
@@ -0,0 +1,272 @@
|
|
1
|
+
require 'socket'
|
2
|
+
require 'getoptlong'
|
3
|
+
|
4
|
+
class ThriftClient
|
5
|
+
|
6
|
+
# This is a simplified form of thrift, useful for clients only, and not
|
7
|
+
# making any attempt to have good performance. It's intended to be used by
|
8
|
+
# small command-line tools that don't want to install a dozen ruby files.
|
9
|
+
module Simple
|
10
|
+
VERSION_1 = 0x8001
|
11
|
+
|
12
|
+
# message types
|
13
|
+
CALL, REPLY, EXCEPTION = (1..3).to_a
|
14
|
+
|
15
|
+
# value types
|
16
|
+
STOP, VOID, BOOL, BYTE, DOUBLE, _, I16, _, I32, _, I64, STRING, STRUCT, MAP, SET, LIST = (0..15).to_a
|
17
|
+
|
18
|
+
FORMATS = {
|
19
|
+
BYTE => "c",
|
20
|
+
DOUBLE => "G",
|
21
|
+
I16 => "n",
|
22
|
+
I32 => "N",
|
23
|
+
}
|
24
|
+
|
25
|
+
SIZES = {
|
26
|
+
BYTE => 1,
|
27
|
+
DOUBLE => 8,
|
28
|
+
I16 => 2,
|
29
|
+
I32 => 4,
|
30
|
+
}
|
31
|
+
|
32
|
+
module ComplexType
|
33
|
+
module Extends
|
34
|
+
def type_id=(n)
|
35
|
+
@type_id = n
|
36
|
+
end
|
37
|
+
|
38
|
+
def type_id
|
39
|
+
@type_id
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
module Includes
|
44
|
+
def to_i
|
45
|
+
self.class.type_id
|
46
|
+
end
|
47
|
+
|
48
|
+
def to_s
|
49
|
+
args = self.values.map { |v| self.class.type_id == STRUCT ? v.name : v.to_s }.join(", ")
|
50
|
+
"#{self.class.name}.new(#{args})"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.make_type(type_id, name, *args)
|
56
|
+
klass = Struct.new("STT_#{name}", *args)
|
57
|
+
klass.send(:extend, ComplexType::Extends)
|
58
|
+
klass.send(:include, ComplexType::Includes)
|
59
|
+
klass.type_id = type_id
|
60
|
+
klass
|
61
|
+
end
|
62
|
+
|
63
|
+
ListType = make_type(LIST, "ListType", :element_type)
|
64
|
+
MapType = make_type(MAP, "MapType", :key_type, :value_type)
|
65
|
+
SetType = make_type(SET, "SetType", :element_type)
|
66
|
+
StructType = make_type(STRUCT, "StructType", :struct_class)
|
67
|
+
|
68
|
+
class << self
|
69
|
+
def pack_value(type, value)
|
70
|
+
case type
|
71
|
+
when BOOL
|
72
|
+
[ value ? 1 : 0 ].pack("c")
|
73
|
+
when STRING
|
74
|
+
[ value.size, value ].pack("Na*")
|
75
|
+
when I64
|
76
|
+
[ value >> 32, value & 0xffffffff ].pack("NN")
|
77
|
+
when ListType
|
78
|
+
[ type.element_type.to_i, value.size ].pack("cN") + value.map { |item| pack_value(type.element_type, item) }.join("")
|
79
|
+
when MapType
|
80
|
+
[ type.key_type.to_i, type.value_type.to_i, value.size ].pack("ccN") + value.map { |k, v| pack_value(type.key_type, k) + pack_value(type.value_type, v) }.join("")
|
81
|
+
when SetType
|
82
|
+
[ type.element_type.to_i, value.size ].pack("cN") + value.map { |item| pack_value(type.element_type, item) }.join("")
|
83
|
+
when StructType
|
84
|
+
value._pack
|
85
|
+
else
|
86
|
+
[ value ].pack(FORMATS[type])
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def pack_request(method_name, arg_struct, request_id=0)
|
91
|
+
[ VERSION_1, CALL, method_name.to_s.size, method_name.to_s, request_id, arg_struct._pack ].pack("nnNa*Na*")
|
92
|
+
end
|
93
|
+
|
94
|
+
def read_value(s, type)
|
95
|
+
case type
|
96
|
+
when BOOL
|
97
|
+
s.read(1).unpack("c").first != 0
|
98
|
+
when STRING
|
99
|
+
len = s.read(4).unpack("N").first
|
100
|
+
s.read(len)
|
101
|
+
when I64
|
102
|
+
hi, lo = s.read(8).unpack("NN")
|
103
|
+
rv = (hi << 32) | lo
|
104
|
+
(rv >= (1 << 63)) ? (rv - (1 << 64)) : rv
|
105
|
+
when LIST
|
106
|
+
read_list(s)
|
107
|
+
when MAP
|
108
|
+
read_map(s)
|
109
|
+
when STRUCT
|
110
|
+
read_struct(s)
|
111
|
+
when ListType
|
112
|
+
read_list(s, type.element_type)
|
113
|
+
when MapType
|
114
|
+
read_map(s, type.key_type, type.value_type)
|
115
|
+
when StructType
|
116
|
+
read_struct(s, type.struct_class)
|
117
|
+
else
|
118
|
+
rv = s.read(SIZES[type]).unpack(FORMATS[type]).first
|
119
|
+
case type
|
120
|
+
when I16
|
121
|
+
(rv >= (1 << 15)) ? (rv - (1 << 16)) : rv
|
122
|
+
when I32
|
123
|
+
(rv >= (1 << 31)) ? (rv - (1 << 32)) : rv
|
124
|
+
else
|
125
|
+
rv
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def read_list(s, element_type=nil)
|
131
|
+
etype, len = s.read(5).unpack("cN")
|
132
|
+
expected_type = (element_type and element_type.to_i == etype.to_i) ? element_type : etype
|
133
|
+
rv = []
|
134
|
+
len.times do
|
135
|
+
rv << read_value(s, expected_type)
|
136
|
+
end
|
137
|
+
rv
|
138
|
+
end
|
139
|
+
|
140
|
+
def read_map(s, key_type=nil, value_type=nil)
|
141
|
+
ktype, vtype, len = s.read(6).unpack("ccN")
|
142
|
+
rv = {}
|
143
|
+
expected_key_type, expected_value_type = if key_type and value_type and key_type.to_i == ktype and value_type.to_i == vtype
|
144
|
+
[ key_type, value_type ]
|
145
|
+
else
|
146
|
+
[ ktype, vtype ]
|
147
|
+
end
|
148
|
+
len.times do
|
149
|
+
key = read_value(s, expected_key_type)
|
150
|
+
value = read_value(s, expected_value_type)
|
151
|
+
rv[key] = value
|
152
|
+
end
|
153
|
+
rv
|
154
|
+
end
|
155
|
+
|
156
|
+
def read_struct(s, struct_class=nil)
|
157
|
+
rv = struct_class.new()
|
158
|
+
while true
|
159
|
+
type = s.read(1).unpack("c").first
|
160
|
+
return rv if type == STOP
|
161
|
+
fid = s.read(2).unpack("n").first
|
162
|
+
field = struct_class ? struct_class._fields.find { |f| (f.fid == fid) and (f.type.to_i == type) } : nil
|
163
|
+
value = read_value(s, field ? field.type : type)
|
164
|
+
rv[field.name] = value if field
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
def read_response(s, rv_class)
|
169
|
+
version, message_type, method_name_len = s.read(8).unpack("nnN")
|
170
|
+
method_name = s.read(method_name_len)
|
171
|
+
seq_id = s.read(4).unpack("N").first
|
172
|
+
[ method_name, seq_id, read_struct(s, rv_class).rv ]
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
## ----------------------------------------
|
177
|
+
|
178
|
+
class Field
|
179
|
+
attr_accessor :name, :type, :fid
|
180
|
+
|
181
|
+
def initialize(name, type, fid)
|
182
|
+
@name = name
|
183
|
+
@type = type
|
184
|
+
@fid = fid
|
185
|
+
end
|
186
|
+
|
187
|
+
def pack(value)
|
188
|
+
value.nil? ? "" : [ type.to_i, fid, ThriftClient::Simple.pack_value(type, value) ].pack("cna*")
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
class ThriftException < RuntimeError
|
193
|
+
def initialize(reason)
|
194
|
+
@reason = reason
|
195
|
+
end
|
196
|
+
|
197
|
+
def to_s
|
198
|
+
"ThriftException(#{@reason.inspect})"
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
module ThriftStruct
|
203
|
+
module Include
|
204
|
+
def _pack
|
205
|
+
self.class._fields.map { |f| f.pack(self[f.name]) }.join + [ STOP ].pack("c")
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
module Extend
|
210
|
+
def _fields
|
211
|
+
@fields
|
212
|
+
end
|
213
|
+
|
214
|
+
def _fields=(f)
|
215
|
+
@fields = f
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
def self.make_struct(name, *fields)
|
221
|
+
st_name = "ST_#{name}"
|
222
|
+
if Struct.constants.include?(st_name)
|
223
|
+
warn "#{caller[0]}: Struct::#{st_name} is already defined; returning original class."
|
224
|
+
Struct.const_get(st_name)
|
225
|
+
else
|
226
|
+
names = fields.map { |f| f.name.to_sym }
|
227
|
+
klass = Struct.new(st_name, *names)
|
228
|
+
klass.send(:include, ThriftStruct::Include)
|
229
|
+
klass.send(:extend, ThriftStruct::Extend)
|
230
|
+
klass._fields = fields
|
231
|
+
klass
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
class ThriftService
|
236
|
+
def initialize(sock)
|
237
|
+
@sock = sock
|
238
|
+
end
|
239
|
+
|
240
|
+
def self._arg_structs
|
241
|
+
@_arg_structs = {} if @_arg_structs.nil?
|
242
|
+
@_arg_structs
|
243
|
+
end
|
244
|
+
|
245
|
+
def self.thrift_method(name, rtype, *args)
|
246
|
+
arg_struct = ThriftClient::Simple.make_struct("Args__#{self.name}__#{name}", *args)
|
247
|
+
rv_struct = ThriftClient::Simple.make_struct("Retval__#{self.name}__#{name}", ThriftClient::Simple::Field.new(:rv, rtype, 0))
|
248
|
+
_arg_structs[name.to_sym] = [ arg_struct, rv_struct ]
|
249
|
+
|
250
|
+
arg_names = args.map { |a| a.name.to_s }.join(", ")
|
251
|
+
class_eval "def #{name}(#{arg_names}); _proxy(:#{name}#{args.size > 0 ? ', ' : ''}#{arg_names}); end"
|
252
|
+
end
|
253
|
+
|
254
|
+
def _proxy(method_name, *args)
|
255
|
+
cls = self.class.ancestors.find { |cls| cls.respond_to?(:_arg_structs) and cls._arg_structs[method_name.to_sym] }
|
256
|
+
arg_class, rv_class = cls._arg_structs[method_name.to_sym]
|
257
|
+
arg_struct = arg_class.new(*args)
|
258
|
+
@sock.write(ThriftClient::Simple.pack_request(method_name, arg_struct))
|
259
|
+
rv = ThriftClient::Simple.read_response(@sock, rv_class)
|
260
|
+
rv[2]
|
261
|
+
end
|
262
|
+
|
263
|
+
# convenience. robey is lazy.
|
264
|
+
[[ :field, "Field.new" ], [ :struct, "StructType.new" ],
|
265
|
+
[ :list, "ListType.new" ], [ :map, "MapType.new" ]].each do |new_name, old_name|
|
266
|
+
class_eval "def self.#{new_name}(*args); ThriftClient::Simple::#{old_name}(*args); end"
|
267
|
+
end
|
268
|
+
|
269
|
+
[ :void, :bool, :byte, :double, :i16, :i32, :i64, :string ].each { |sym| class_eval "def self.#{sym}; ThriftClient::Simple::#{sym.to_s.upcase}; end" }
|
270
|
+
end
|
271
|
+
end
|
272
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Thrift
|
2
|
+
class BaseTransport
|
3
|
+
def timeout=(timeout)
|
4
|
+
end
|
5
|
+
|
6
|
+
def timeout
|
7
|
+
nil
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
class BufferedTransport
|
12
|
+
def timeout=(timeout)
|
13
|
+
@transport.timeout = timeout
|
14
|
+
end
|
15
|
+
|
16
|
+
def timeout
|
17
|
+
@transport.timeout
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class FramedTransport
|
22
|
+
def timeout=(timeout)
|
23
|
+
@transport.timeout = timeout
|
24
|
+
end
|
25
|
+
|
26
|
+
def timeout
|
27
|
+
@transport.timeout
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
module Client
|
32
|
+
def timeout=(timeout)
|
33
|
+
@iprot.trans.timeout = timeout
|
34
|
+
end
|
35
|
+
|
36
|
+
def timeout
|
37
|
+
@iprot.trans.timeout
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
#
|
2
|
+
# Autogenerated by Thrift
|
3
|
+
#
|
4
|
+
# DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
|
5
|
+
#
|
6
|
+
|
7
|
+
require 'thrift'
|
8
|
+
|
9
|
+
module Greeter
|
10
|
+
class Client
|
11
|
+
include ::Thrift::Client
|
12
|
+
|
13
|
+
def greeting(name)
|
14
|
+
send_greeting(name)
|
15
|
+
return recv_greeting()
|
16
|
+
end
|
17
|
+
|
18
|
+
def send_greeting(name)
|
19
|
+
send_message('greeting', Greeting_args, :name => name)
|
20
|
+
end
|
21
|
+
|
22
|
+
def recv_greeting()
|
23
|
+
result = receive_message(Greeting_result)
|
24
|
+
return result.success unless result.success.nil?
|
25
|
+
raise ::Thrift::ApplicationException.new(::Thrift::ApplicationException::MISSING_RESULT, 'greeting failed: unknown result')
|
26
|
+
end
|
27
|
+
|
28
|
+
def yo(name)
|
29
|
+
send_yo(name)
|
30
|
+
end
|
31
|
+
|
32
|
+
def send_yo(name)
|
33
|
+
send_message('yo', Yo_args, :name => name)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
class Processor
|
38
|
+
include ::Thrift::Processor
|
39
|
+
|
40
|
+
def process_greeting(seqid, iprot, oprot)
|
41
|
+
args = read_args(iprot, Greeting_args)
|
42
|
+
result = Greeting_result.new()
|
43
|
+
result.success = @handler.greeting(args.name)
|
44
|
+
write_result(result, oprot, 'greeting', seqid)
|
45
|
+
end
|
46
|
+
|
47
|
+
def process_yo(seqid, iprot, oprot)
|
48
|
+
args = read_args(iprot, Yo_args)
|
49
|
+
@handler.yo(args.name)
|
50
|
+
return
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
# HELPER FUNCTIONS AND STRUCTURES
|
56
|
+
|
57
|
+
class Greeting_args
|
58
|
+
include ::Thrift::Struct, ::Thrift::Struct_Union
|
59
|
+
NAME = 1
|
60
|
+
|
61
|
+
FIELDS = {
|
62
|
+
NAME => {:type => ::Thrift::Types::STRING, :name => 'name'}
|
63
|
+
}
|
64
|
+
|
65
|
+
def struct_fields; FIELDS; end
|
66
|
+
|
67
|
+
def validate
|
68
|
+
end
|
69
|
+
|
70
|
+
::Thrift::Struct.generate_accessors self
|
71
|
+
end
|
72
|
+
|
73
|
+
class Greeting_result
|
74
|
+
include ::Thrift::Struct, ::Thrift::Struct_Union
|
75
|
+
SUCCESS = 0
|
76
|
+
|
77
|
+
FIELDS = {
|
78
|
+
SUCCESS => {:type => ::Thrift::Types::STRING, :name => 'success'}
|
79
|
+
}
|
80
|
+
|
81
|
+
def struct_fields; FIELDS; end
|
82
|
+
|
83
|
+
def validate
|
84
|
+
end
|
85
|
+
|
86
|
+
::Thrift::Struct.generate_accessors self
|
87
|
+
end
|
88
|
+
|
89
|
+
class Yo_args
|
90
|
+
include ::Thrift::Struct, ::Thrift::Struct_Union
|
91
|
+
NAME = 1
|
92
|
+
|
93
|
+
FIELDS = {
|
94
|
+
NAME => {:type => ::Thrift::Types::STRING, :name => 'name'}
|
95
|
+
}
|
96
|
+
|
97
|
+
def struct_fields; FIELDS; end
|
98
|
+
|
99
|
+
def validate
|
100
|
+
end
|
101
|
+
|
102
|
+
::Thrift::Struct.generate_accessors self
|
103
|
+
end
|
104
|
+
|
105
|
+
class Yo_result
|
106
|
+
include ::Thrift::Struct, ::Thrift::Struct_Union
|
107
|
+
|
108
|
+
FIELDS = {
|
109
|
+
|
110
|
+
}
|
111
|
+
|
112
|
+
def struct_fields; FIELDS; end
|
113
|
+
|
114
|
+
def validate
|
115
|
+
end
|
116
|
+
|
117
|
+
::Thrift::Struct.generate_accessors self
|
118
|
+
end
|
119
|
+
|
120
|
+
end
|
121
|
+
|