thrift 0.22.0 → 0.24.0

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.
Files changed (119) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +235 -17
  3. data/benchmark/benchmark.rb +23 -8
  4. data/benchmark/client.rb +50 -6
  5. data/benchmark/server.rb +46 -7
  6. data/benchmark/thin_server.rb +2 -0
  7. data/ext/binary_protocol_accelerated.c +154 -42
  8. data/ext/bytes.c +14 -0
  9. data/ext/compact_protocol.c +138 -45
  10. data/ext/constants.h +12 -0
  11. data/ext/extconf.rb +17 -8
  12. data/ext/memory_buffer.c +44 -7
  13. data/ext/protocol.c +29 -0
  14. data/ext/protocol.h +35 -0
  15. data/ext/struct.c +48 -17
  16. data/ext/thrift_native.c +28 -3
  17. data/lib/thrift/bytes.rb +70 -100
  18. data/lib/thrift/client.rb +54 -13
  19. data/lib/thrift/exceptions.rb +6 -5
  20. data/lib/thrift/multiplexed_processor.rb +20 -10
  21. data/lib/thrift/processor.rb +7 -6
  22. data/lib/thrift/protocol/base_protocol.rb +52 -21
  23. data/lib/thrift/protocol/binary_protocol.rb +65 -20
  24. data/lib/thrift/protocol/binary_protocol_accelerated.rb +6 -5
  25. data/lib/thrift/protocol/compact_protocol.rb +76 -41
  26. data/lib/thrift/protocol/header_protocol.rb +321 -0
  27. data/lib/thrift/protocol/json_protocol.rb +44 -27
  28. data/lib/thrift/protocol/multiplexed_protocol.rb +6 -5
  29. data/lib/thrift/protocol/protocol_decorator.rb +13 -4
  30. data/lib/thrift/serializer/deserializer.rb +6 -5
  31. data/lib/thrift/serializer/serializer.rb +5 -5
  32. data/lib/thrift/server/base_server.rb +5 -4
  33. data/lib/thrift/server/mongrel_http_server.rb +7 -6
  34. data/lib/thrift/server/nonblocking_server.rb +35 -9
  35. data/lib/thrift/server/simple_server.rb +13 -5
  36. data/lib/thrift/server/thin_http_server.rb +4 -3
  37. data/lib/thrift/server/thread_pool_server.rb +7 -6
  38. data/lib/thrift/server/threaded_server.rb +13 -5
  39. data/lib/thrift/struct.rb +12 -11
  40. data/lib/thrift/struct_union.rb +14 -9
  41. data/lib/thrift/thrift_native.rb +3 -2
  42. data/lib/thrift/transport/base_server_transport.rb +8 -5
  43. data/lib/thrift/transport/base_transport.rb +18 -14
  44. data/lib/thrift/transport/buffered_transport.rb +7 -6
  45. data/lib/thrift/transport/framed_transport.rb +8 -7
  46. data/lib/thrift/transport/header_transport.rb +562 -0
  47. data/lib/thrift/transport/http_client_transport.rb +2 -1
  48. data/lib/thrift/transport/io_stream_transport.rb +4 -3
  49. data/lib/thrift/transport/memory_buffer_transport.rb +13 -6
  50. data/lib/thrift/transport/server_socket.rb +14 -8
  51. data/lib/thrift/transport/socket.rb +126 -60
  52. data/lib/thrift/transport/ssl_server_socket.rb +4 -3
  53. data/lib/thrift/transport/ssl_socket.rb +45 -13
  54. data/lib/thrift/transport/unix_server_socket.rb +9 -5
  55. data/lib/thrift/transport/unix_socket.rb +7 -6
  56. data/lib/thrift/types.rb +10 -6
  57. data/lib/thrift/union.rb +15 -8
  58. data/lib/thrift/uuid.rb +50 -0
  59. data/lib/thrift.rb +4 -1
  60. data/spec/ThriftSpec.thrift +21 -1
  61. data/spec/base_protocol_spec.rb +21 -2
  62. data/spec/base_transport_spec.rb +48 -8
  63. data/spec/binary_protocol_accelerated_spec.rb +1 -0
  64. data/spec/binary_protocol_spec.rb +1 -2
  65. data/spec/binary_protocol_spec_shared.rb +206 -155
  66. data/spec/bytes_spec.rb +71 -114
  67. data/spec/client_spec.rb +86 -19
  68. data/spec/compact_protocol_spec.rb +153 -16
  69. data/spec/constants_demo_spec.rb +102 -0
  70. data/spec/exception_spec.rb +1 -1
  71. data/spec/flat_spec.rb +1 -0
  72. data/spec/header_protocol_spec.rb +476 -0
  73. data/spec/header_transport_spec.rb +431 -0
  74. data/spec/http_client_spec.rb +5 -6
  75. data/spec/json_protocol_spec.rb +69 -47
  76. data/spec/multiplexed_processor_spec.rb +75 -0
  77. data/spec/namespaced_spec.rb +1 -1
  78. data/spec/nonblocking_server_spec.rb +174 -8
  79. data/spec/processor_spec.rb +1 -1
  80. data/spec/recursion_depth_spec.rb +223 -0
  81. data/spec/serializer_spec.rb +1 -1
  82. data/spec/server_socket_spec.rb +38 -1
  83. data/spec/server_spec.rb +60 -9
  84. data/spec/socket_spec.rb +119 -13
  85. data/spec/socket_spec_shared.rb +73 -9
  86. data/spec/spec_helper.rb +2 -1
  87. data/spec/ssl_server_socket_spec.rb +52 -1
  88. data/spec/ssl_socket_spec.rb +181 -11
  89. data/spec/struct_nested_containers_spec.rb +2 -2
  90. data/spec/struct_spec.rb +114 -9
  91. data/spec/support/header_protocol_helper.rb +55 -0
  92. data/spec/thin_http_server_spec.rb +4 -18
  93. data/spec/types_spec.rb +26 -26
  94. data/spec/union_spec.rb +70 -11
  95. data/spec/unix_socket_spec.rb +17 -2
  96. data/spec/uuid_validation_spec.rb +239 -0
  97. data/test/fuzz/Makefile +779 -0
  98. data/test/fuzz/Makefile.am +173 -0
  99. data/test/fuzz/Makefile.in +775 -0
  100. data/test/fuzz/README.md +149 -0
  101. data/test/fuzz/fuzz_common.rb +96 -0
  102. data/{lib/thrift/core_ext.rb → test/fuzz/fuzz_parse_binary_protocol.rb} +4 -4
  103. data/{lib/thrift/core_ext/fixnum.rb → test/fuzz/fuzz_parse_binary_protocol_accelerated.rb} +7 -13
  104. data/test/fuzz/fuzz_parse_binary_protocol_accelerated_harness.rb +23 -0
  105. data/test/fuzz/fuzz_parse_binary_protocol_harness.rb +23 -0
  106. data/test/fuzz/fuzz_parse_compact_protocol.rb +23 -0
  107. data/test/fuzz/fuzz_parse_compact_protocol_harness.rb +23 -0
  108. data/test/fuzz/fuzz_parse_json_protocol.rb +23 -0
  109. data/test/fuzz/fuzz_parse_json_protocol_harness.rb +23 -0
  110. data/test/fuzz/fuzz_roundtrip_binary_protocol.rb +23 -0
  111. data/test/fuzz/fuzz_roundtrip_binary_protocol_accelerated.rb +23 -0
  112. data/test/fuzz/fuzz_roundtrip_binary_protocol_accelerated_harness.rb +23 -0
  113. data/test/fuzz/fuzz_roundtrip_binary_protocol_harness.rb +23 -0
  114. data/test/fuzz/fuzz_roundtrip_compact_protocol.rb +23 -0
  115. data/test/fuzz/fuzz_roundtrip_compact_protocol_harness.rb +23 -0
  116. data/test/fuzz/fuzz_roundtrip_json_protocol.rb +23 -0
  117. data/test/fuzz/fuzz_roundtrip_json_protocol_harness.rb +23 -0
  118. data/test/fuzz/fuzz_tracer.rb +29 -0
  119. metadata +105 -70
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fe32174805f7e6b9801ebf00500fad76c1922c3f5e816fba597dfc0aae195c5d
4
- data.tar.gz: b908a7a66b4bff7fc95728778240d2583d685f2fdcdf563edb125dc571cb0040
3
+ metadata.gz: 90306a751b8d883922c4556778391d56c8c48fbf008187d3edfe411db4e9f6f7
4
+ data.tar.gz: 95287ba8714521389775a7d2ad265b3ad60a3ca44ada102abcf0c2ee152347cd
5
5
  SHA512:
6
- metadata.gz: cd8377282c40257b9d4bd2e95633584a04f27609118a8694d9a0037cbf9affc4d9f42aa4647c4dcd43ed64668a77f7abb375f77ca6e5bef8aaae03db8702dcd8
7
- data.tar.gz: 31637b8001126ee81c9a74803e2cbb942cb57dfbc529a72ceb121a28a2614695689d55bd0204dc12429d82ce38a7c0057c18420a872921f376e20e178b213c5e
6
+ metadata.gz: d7e052575e5b7e581440a9073d2f4c6ce828b79963f7ce5c077f77e7c759b3589d5671a9e6f37ff4df996061af6b77362cc6248c7d22ac414156594b3fd77339
7
+ data.tar.gz: 35f5ee1d30099e5c70fa49d420153442e2b0b97587a908dea86ad1f9a2bbc44df5ff1baa3474cbc48f4064e20571eab7aa00c5fb155202f05edcd17579cbaddf
data/README.md CHANGED
@@ -1,7 +1,6 @@
1
- Thrift Ruby Software Library
2
- http://thrift.apache.org
1
+ # Thrift Ruby Software Library
3
2
 
4
- == LICENSE:
3
+ ## License
5
4
 
6
5
  Licensed to the Apache Software Foundation (ASF) under one
7
6
  or more contributor license agreements. See the NOTICE file
@@ -20,24 +19,243 @@ KIND, either express or implied. See the License for the
20
19
  specific language governing permissions and limitations
21
20
  under the License.
22
21
 
23
- == DESCRIPTION:
22
+ # Using Thrift with Ruby
24
23
 
25
- Thrift is a strongly-typed language-agnostic RPC system.
26
- This library is the ruby implementation for both clients and servers.
24
+ Ruby bindings for the Apache Thrift RPC system. The gem contains the runtime
25
+ types, transports, protocols, and servers used by generated Ruby code for both
26
+ clients and services.
27
27
 
28
- == INSTALL:
28
+ ## Compatibility
29
29
 
30
- $ gem install thrift
30
+ - Ruby MRI >= 2.7 (tested against current supported releases).
31
+ - JRuby works with the pure-Ruby implementation; the native extension is
32
+ skipped automatically.
33
+ - For the repo-wide transport, protocol, and server support matrix, see
34
+ [Language Feature Matrix](https://github.com/apache/thrift/blob/master/LANGUAGES.md). This README focuses on Ruby-specific
35
+ behavior and migration notes.
31
36
 
32
- == CAVEATS:
37
+ ## Installation
33
38
 
34
- This library provides the client and server implementations of thrift.
35
- It does <em>not</em> provide the compiler for the .thrift files. To compile
36
- .thrift files into language-specific implementations, please download the full
37
- thrift software package.
39
+ - Requirements: Ruby >= 2.7.
40
+ - From RubyGems: `gem install thrift`
41
+ - From source: `bundle install`, `gem build thrift.gemspec`, then install the
42
+ resulting `thrift-*.gem`. The native accelerator is built when the gem is
43
+ installed on supported runtimes.
38
44
 
39
- == USAGE:
45
+ ## Generating Ruby Code
40
46
 
41
- This section should get written by someone with the time and inclination.
42
- In the meantime, look at existing code, such as the benchmark or the tutorial
43
- in the full thrift distribution.
47
+ The Ruby library does not include the Thrift compiler. Use a compiler built
48
+ from the root of this repository to generate Ruby bindings:
49
+
50
+ ```bash
51
+ thrift --gen rb path/to/service.thrift
52
+ # with namespaced modules
53
+ thrift --gen rb:namespaced --recurse path/to/service.thrift
54
+ ```
55
+
56
+ Generated files are typically written to `gen-rb/` and can be required
57
+ directly from your application.
58
+
59
+ ## Basic Client Usage
60
+
61
+ ```ruby
62
+ $:.push File.expand_path('gen-rb', __dir__)
63
+ require 'thrift'
64
+ require 'calculator'
65
+
66
+ socket = Thrift::Socket.new('localhost', 9090)
67
+ transport = Thrift::BufferedTransport.new(socket)
68
+ protocol = Thrift::BinaryProtocol.new(transport)
69
+ client = Calculator::Client.new(protocol)
70
+
71
+ transport.open
72
+ puts client.add(1, 1)
73
+ transport.close
74
+ ```
75
+
76
+ ## Basic Server Usage
77
+
78
+ ```ruby
79
+ $:.push File.expand_path('gen-rb', __dir__)
80
+ require 'thrift'
81
+ require 'calculator'
82
+
83
+ class CalculatorHandler
84
+ def add(a, b)
85
+ a + b
86
+ end
87
+ end
88
+
89
+ handler = CalculatorHandler.new
90
+ processor = Calculator::Processor.new(handler)
91
+ server_transport = Thrift::ServerSocket.new(9090)
92
+ transport_factory = Thrift::BufferedTransportFactory.new
93
+ protocol_factory = Thrift::BinaryProtocolFactory.new
94
+
95
+ server = Thrift::ThreadedServer.new(processor, server_transport,
96
+ transport_factory, protocol_factory)
97
+ server.serve
98
+ ```
99
+
100
+ ## Development and Tests
101
+
102
+ - `bundle exec rake spec` runs the Ruby specs. It expects a built Thrift
103
+ compiler at `../../compiler/cpp/thrift`.
104
+ - `bundle exec rake test` runs the cross-language test suite; it must be
105
+ executed from a full Thrift checkout.
106
+ - `bundle exec rake build_ext` (implicit in the tasks above) compiles the
107
+ optional native extension that accelerates protocols and buffers.
108
+
109
+ ## More Ruby Code
110
+
111
+ - Tutorial client and server: `tutorial/rb/RubyClient.rb` and `tutorial/rb/RubyServer.rb`
112
+ - Runtime benchmarks: `lib/rb/benchmark`
113
+ - Protocol benchmark: `test/rb/benchmarks/protocol_benchmark.rb`
114
+ - Library specs: `lib/rb/spec`
115
+ - Fuzzing harnesses and notes: `lib/rb/test/fuzz`
116
+ - Cross-language and integration tests: `test/rb`
117
+
118
+ ## Breaking Changes
119
+
120
+ ### 0.24.0
121
+
122
+ Connect timeout handling changed for both `Thrift::Socket` and
123
+ `Thrift::SSLSocket`.
124
+
125
+ - `timeout == nil` and `timeout == 0` now use blocking connect/open semantics.
126
+ Older releases already treated `nil` that way, but treated `0` differently
127
+ across operations: `read` and `write` used the blocking path, plain TCP
128
+ `open` used a zero-length poll, and TLS `open` could spin in the handshake
129
+ retry loop.
130
+ - Positive timeouts now bound the whole connect/open operation instead of
131
+ effectively applying a fresh timeout window at each wait or address attempt.
132
+ For plain TCP this budget is shared across address fallback. For TLS it is
133
+ shared across the TCP connect and the SSL handshake.
134
+ - Connect/open timeout expiry now raises
135
+ `Thrift::TransportException::TIMED_OUT`. Older releases could report the same
136
+ condition as `NOT_OPEN`, and `Thrift::SSLSocket` could keep retrying the
137
+ handshake after the wait timed out.
138
+
139
+ If your application matched `NOT_OPEN` for connect timeout handling, update it
140
+ to handle `TIMED_OUT`. If you relied on `timeout = 0` meaning immediate failure
141
+ or on repeated retries extending the effective timeout during TCP fallback or
142
+ TLS handshake, update those call paths before upgrading.
143
+
144
+ Ruby server socket transports now apply a 5-second timeout to accepted client
145
+ sockets by default. This prevents stalled clients from blocking server threads
146
+ indefinitely during response writes. Applications that intentionally require
147
+ blocking accepted sockets can pass `client_timeout: nil` or `client_timeout: 0`
148
+ when constructing `Thrift::ServerSocket`, `Thrift::SSLServerSocket`, or
149
+ `Thrift::UNIXServerSocket`.
150
+
151
+ Generated Ruby structs and unions now suffix field ID constants as
152
+ `*_FIELD_ID` instead of exposing bare uppercased field names. For example,
153
+ `MyStruct::FOO` becomes `MyStruct::FOO_FIELD_ID`. This avoids collisions with
154
+ the generated `FIELDS` metadata hash for field names such as `fields`, but it
155
+ is a source-compatible break if your application referenced the old constants
156
+ directly. Regenerate Ruby code and update those constant references atomically.
157
+
158
+ Ruby binary protocol writers now enforce signed Thrift integer ranges before
159
+ serializing values. `byte`, `i16`, `i32`, and `i64` writes reject values outside
160
+ their declared signed ranges, and binary/string/container sizes must fit in a
161
+ non-negative signed `i32` length. Older releases could silently wrap or clip
162
+ some out-of-range values, such as writing `255` as a byte that read back as
163
+ `-1`. This applies to both `Thrift::BinaryProtocol` and
164
+ `Thrift::BinaryProtocolAccelerated`.
165
+
166
+ ### 0.23.0
167
+
168
+ The documented source-build flow now effectively requires Ruby `2.7+`.
169
+ The committed development bundle no longer resolves on Ruby `2.6`
170
+ (`json-2.18.1 requires ruby version >= 2.7`), so building and testing this
171
+ library from source should be treated as `2.7+`.
172
+
173
+ Generated structs and unions now consistently raise
174
+ `Thrift::ProtocolException::INVALID_DATA` for invalid payloads such as unset
175
+ required fields, invalid enum values, or invalid union state. If your
176
+ application or tests matched older exception types or messages, update them.
177
+
178
+ Regenerated Ruby clients now validate replies more strictly. Mismatched reply
179
+ message types, method names, or sequence IDs raise
180
+ `Thrift::ApplicationException::INVALID_MESSAGE_TYPE`,
181
+ `Thrift::ApplicationException::WRONG_METHOD_NAME`, or
182
+ `Thrift::ApplicationException::BAD_SEQUENCE_ID`. If you relied on older,
183
+ looser reply handling in servers, proxies, or tests, regenerate and update
184
+ those call paths together.
185
+
186
+ Generated Ruby clients have never been safe to share across concurrent
187
+ threads. A client tracks pending sequence IDs on a single reply stream, so use
188
+ one client/transport pair per thread or serialize access yourself.
189
+
190
+ Treat `Thrift::ApplicationException::BAD_SEQUENCE_ID` as a correctness bug
191
+ that needs immediate attention. It means the client read a reply whose
192
+ sequence ID did not match the next pending request, so the connection may
193
+ already be out of sync and you may be reading a reply intended for a
194
+ different call. The most common cause is sharing one client across threads,
195
+ but a buggy proxy or server can also cause it.
196
+
197
+ ### 0.13.0
198
+
199
+ Ruby development and CI moved to Ruby `2.4+`, but the runtime still claimed
200
+ support for older interpreters. Treat Ruby `< 2.4` on the `0.13.x` line as
201
+ best-effort, not guaranteed.
202
+
203
+ - Historical note for very old releases: the Ruby runtime was rearranged to use
204
+ more Ruby-like names, and generated files switched to underscored filenames.
205
+ If you are upgrading very old code, regenerate your Ruby bindings and update
206
+ any old `T*` constants or legacy require paths such as
207
+ `TBinaryProtocol` -> `Thrift::BinaryProtocol`.
208
+ - `rb:namespaced` changes the generated file layout. Flat output from
209
+ `thrift --gen rb` and namespaced output from `thrift --gen rb:namespaced`
210
+ use different require paths, so switch them atomically with regenerated code.
211
+
212
+ ```ruby
213
+ # --gen rb
214
+ require 'calculator'
215
+
216
+ # --gen rb:namespaced
217
+ require 'my_namespace/calculator'
218
+ ```
219
+
220
+ ## Migration Notes
221
+
222
+ - If you upgrade to `0.24.0`, treat `timeout` on `Thrift::Socket` and
223
+ `Thrift::SSLSocket` as one budget for the whole open path. For
224
+ `Thrift::SSLSocket`, that includes both the TCP connect and the TLS
225
+ handshake.
226
+ - If you upgrade to `0.24.0`, handle connect/open timeout expiry as
227
+ `Thrift::TransportException::TIMED_OUT` instead of `NOT_OPEN`.
228
+ - If you upgrade across the stricter reply-validation changes, regenerate all
229
+ Ruby stubs and deploy them with the matching Ruby runtime. Do not mix old
230
+ generated code, new generated code, and new runtime code on the same client
231
+ path without testing that combination.
232
+ - If you receive `Thrift::ApplicationException::BAD_SEQUENCE_ID`, treat the
233
+ connection as out of sync. Close it, create a new client/transport pair, and
234
+ investigate the root cause before retrying.
235
+ - Do not share one generated Ruby client across concurrent threads. Use one
236
+ client/transport pair per thread, or serialize access to a shared client.
237
+ - If you switch between `thrift --gen rb` and `thrift --gen rb:namespaced`,
238
+ regenerate all Ruby output and update `require` paths in the same change.
239
+
240
+ ## Runtime Notes
241
+
242
+ - Loading the `thrift_native` extension changes which implementation you are
243
+ running. It replaces
244
+ `Thrift::Struct`, `Thrift::Union`, and `Thrift::CompactProtocol` methods
245
+ with C implementations in place. `Thrift::BinaryProtocol` remains available
246
+ in pure Ruby, and the C-backed binary protocol is opt-in through
247
+ `Thrift::BinaryProtocolAcceleratedFactory` or
248
+ `Thrift::BinaryProtocolAccelerated` when that class is available.
249
+ - The native extension is optional. If it cannot be built or loaded, Thrift
250
+ falls back to the pure-Ruby implementation. This mainly changes performance
251
+ and implementation details.
252
+ - JRuby skips the native extension automatically and uses the pure-Ruby path.
253
+ - Do not share one client instance across concurrent threads. A client tracks
254
+ request and reply state on a single transport stream.
255
+ - `Thrift::NonblockingServer` expects framed input. Use
256
+ `Thrift::FramedTransport` with it on the wire.
257
+ - Client and server must agree on transport and protocol choices. If you
258
+ switch to SSL, HTTP, header transport, compact protocol, or namespaced
259
+ generated code, update both ends together.
260
+ - HTTPS client transport verifies peers by default, and `Thrift::SSLSocket`
261
+ performs a hostname check against the host you pass in.
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  #
2
3
  # Licensed to the Apache Software Foundation (ASF) under one
3
4
  # or more contributor license agreements. See the NOTICE file
@@ -19,6 +20,7 @@
19
20
 
20
21
  require 'rubygems'
21
22
  $:.unshift File.dirname(__FILE__) + '/../lib'
23
+ $:.unshift File.dirname(__FILE__) + '/../ext'
22
24
  require 'thrift'
23
25
  require 'stringio'
24
26
 
@@ -34,18 +36,21 @@ class Server
34
36
  attr_accessor :interpreter
35
37
  attr_accessor :host
36
38
  attr_accessor :port
39
+ attr_accessor :protocol_type
37
40
 
38
41
  def initialize(opts)
39
42
  @serverclass = opts.fetch(:class, Thrift::NonblockingServer)
40
43
  @interpreter = opts.fetch(:interpreter, "ruby")
41
44
  @host = opts.fetch(:host, ::HOST)
42
45
  @port = opts.fetch(:port, ::PORT)
46
+ @protocol_type = opts.fetch(:protocol_type, 'binary')
47
+ @tls = opts.fetch(:tls, false)
43
48
  end
44
49
 
45
50
  def start
46
51
  return if @serverclass == Object
47
52
  args = (File.basename(@interpreter) == "jruby" ? "-J-server" : "")
48
- @pipe = IO.popen("#{@interpreter} #{args} #{File.dirname(__FILE__)}/server.rb #{@host} #{@port} #{@serverclass.name}", "r+")
53
+ @pipe = IO.popen("#{@interpreter} #{args} #{File.dirname(__FILE__)}/server.rb #{"-tls" if @tls} #{@host} #{@port} #{@serverclass.name} #{@protocol_type}", "r+")
49
54
  Marshal.load(@pipe) # wait until the server has started
50
55
  sleep 0.4 # give the server time to actually start spawning sockets
51
56
  end
@@ -75,6 +80,8 @@ class BenchmarkManager
75
80
  @interpreter = opts.fetch(:interpreter, "ruby")
76
81
  @server = server
77
82
  @log_exceptions = opts.fetch(:log_exceptions, false)
83
+ @protocol_type = opts.fetch(:protocol_type, 'binary')
84
+ @tls = opts.fetch(:tls, false)
78
85
  end
79
86
 
80
87
  def run
@@ -93,13 +100,15 @@ class BenchmarkManager
93
100
  end
94
101
 
95
102
  def spawn
96
- pipe = IO.popen("#{@interpreter} #{File.dirname(__FILE__)}/client.rb #{"-log-exceptions" if @log_exceptions} #{@host} #{@port} #{@clients_per_process} #{@calls_per_client}")
103
+ pipe = IO.popen("#{@interpreter} #{File.dirname(__FILE__)}/client.rb #{"-log-exceptions" if @log_exceptions} #{"-tls" if @tls} #{@host} #{@port} #{@clients_per_process} #{@calls_per_client} #{@protocol_type}")
97
104
  @pool << pipe
98
105
  end
99
106
 
100
107
  def socket_class
101
108
  if @socket
102
109
  Thrift::UNIXSocket
110
+ elsif @tls
111
+ Thrift::SSLSocket
103
112
  else
104
113
  Thrift::Socket
105
114
  end
@@ -108,7 +117,7 @@ class BenchmarkManager
108
117
  def collect_output
109
118
  puts "Collecting output..."
110
119
  # read from @pool until all sockets are closed
111
- @buffers = Hash.new { |h,k| h[k] = '' }
120
+ @buffers = Hash.new { |h, k| h[k] = ''.b }
112
121
  until @pool.empty?
113
122
  rd, = select(@pool)
114
123
  next if rd.nil?
@@ -176,9 +185,9 @@ class BenchmarkManager
176
185
  end
177
186
  end
178
187
  @report = {}
179
- @report[:total_calls] = call_times.inject(0.0) { |a,t| a += t }
188
+ @report[:total_calls] = call_times.inject(0.0) { |a, t| a += t }
180
189
  @report[:avg_calls] = @report[:total_calls] / call_times.size
181
- @report[:total_clients] = client_times.inject(0.0) { |a,t| a += t }
190
+ @report[:total_clients] = client_times.inject(0.0) { |a, t| a += t }
182
191
  @report[:avg_clients] = @report[:total_clients] / client_times.size
183
192
  @report[:connection_failures] = connection_failures.size
184
193
  @report[:connection_errors] = connection_errors.size
@@ -197,6 +206,7 @@ class BenchmarkManager
197
206
  [["Server class", "%s"], @server.serverclass == Object ? "" : @server.serverclass],
198
207
  [["Server interpreter", "%s"], @server.interpreter],
199
208
  [["Client interpreter", "%s"], @interpreter],
209
+ [["Protocol type", "%s"], @protocol_type],
200
210
  [["Socket class", "%s"], socket_class],
201
211
  ["Number of processes", @num_processes],
202
212
  ["Clients per process", @clients_per_process],
@@ -232,8 +242,8 @@ class BenchmarkManager
232
242
 
233
243
  def tabulate(fmt, *labels_and_values)
234
244
  labels = labels_and_values.map { |l| Array === l ? l.first : l }
235
- label_width = labels.inject(0) { |w,l| l.size > w ? l.size : w }
236
- labels_and_values.each do |(l,v)|
245
+ label_width = labels.inject(0) { |w, l| l.size > w ? l.size : w }
246
+ labels_and_values.each do |(l, v)|
237
247
  f = fmt
238
248
  l, f, c = l if Array === l
239
249
  fmtstr = "%-#{label_width+1}s #{f}"
@@ -246,26 +256,31 @@ class BenchmarkManager
246
256
  end
247
257
 
248
258
  def resolve_const(const)
249
- const and const.split('::').inject(Object) { |k,c| k.const_get(c) }
259
+ const and const.split('::').inject(Object) { |k, c| k.const_get(c) }
250
260
  end
251
261
 
252
262
  puts "Starting server..."
263
+ protocol_type = ENV['THRIFT_PROTOCOL'] || 'binary'
253
264
  args = {}
254
265
  args[:interpreter] = ENV['THRIFT_SERVER_INTERPRETER'] || ENV['THRIFT_INTERPRETER'] || "ruby"
255
266
  args[:class] = resolve_const(ENV['THRIFT_SERVER']) || Thrift::NonblockingServer
256
267
  args[:host] = ENV['THRIFT_HOST'] || HOST
257
268
  args[:port] = (ENV['THRIFT_PORT'] || PORT).to_i
269
+ args[:tls] = ENV['THRIFT_TLS'] == 'true'
270
+ args[:protocol_type] = protocol_type
258
271
  server = Server.new(args)
259
272
  server.start
260
273
 
261
274
  args = {}
262
275
  args[:host] = ENV['THRIFT_HOST'] || HOST
263
276
  args[:port] = (ENV['THRIFT_PORT'] || PORT).to_i
277
+ args[:tls] = ENV['THRIFT_TLS'] == 'true'
264
278
  args[:num_processes] = (ENV['THRIFT_NUM_PROCESSES'] || 40).to_i
265
279
  args[:clients_per_process] = (ENV['THRIFT_NUM_CLIENTS'] || 5).to_i
266
280
  args[:calls_per_client] = (ENV['THRIFT_NUM_CALLS'] || 50).to_i
267
281
  args[:interpreter] = ENV['THRIFT_CLIENT_INTERPRETER'] || ENV['THRIFT_INTERPRETER'] || "ruby"
268
282
  args[:log_exceptions] = !!ENV['THRIFT_LOG_EXCEPTIONS']
283
+ args[:protocol_type] = protocol_type
269
284
  BenchmarkManager.new(args, server).run
270
285
 
271
286
  server.shutdown
data/benchmark/client.rb CHANGED
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  #
2
3
  # Licensed to the Apache Software Foundation (ASF) under one
3
4
  # or more contributor license agreements. See the NOTICE file
@@ -18,24 +19,66 @@
18
19
  #
19
20
 
20
21
  $:.unshift File.dirname(__FILE__) + '/../lib'
22
+ $:.unshift File.dirname(__FILE__) + '/../ext'
21
23
  require 'thrift'
24
+ require 'openssl'
22
25
  $:.unshift File.dirname(__FILE__) + "/gen-rb"
23
26
  require 'benchmark_service'
24
27
 
25
28
  class Client
26
- def initialize(host, port, clients_per_process, calls_per_client, log_exceptions)
29
+ def initialize(host, port, clients_per_process, calls_per_client, log_exceptions, tls, protocol_type)
27
30
  @host = host
28
31
  @port = port
29
32
  @clients_per_process = clients_per_process
30
33
  @calls_per_client = calls_per_client
31
34
  @log_exceptions = log_exceptions
35
+ @tls = tls
36
+ @protocol_type = protocol_type || 'binary'
37
+ end
38
+
39
+ def create_protocol(socket)
40
+ case @protocol_type
41
+ when 'binary'
42
+ transport = Thrift::FramedTransport.new(socket)
43
+ Thrift::BinaryProtocol.new(transport)
44
+ when 'compact'
45
+ transport = Thrift::FramedTransport.new(socket)
46
+ Thrift::CompactProtocol.new(transport)
47
+ when 'header'
48
+ Thrift::HeaderProtocol.new(socket)
49
+ when 'header-compact'
50
+ Thrift::HeaderProtocol.new(socket, nil, Thrift::HeaderSubprotocolID::COMPACT)
51
+ when 'header-zlib'
52
+ protocol = Thrift::HeaderProtocol.new(socket)
53
+ protocol.add_transform(Thrift::HeaderTransformID::ZLIB)
54
+ protocol
55
+ else
56
+ transport = Thrift::FramedTransport.new(socket)
57
+ Thrift::BinaryProtocol.new(transport)
58
+ end
32
59
  end
33
60
 
34
61
  def run
35
62
  @clients_per_process.times do
36
- socket = Thrift::Socket.new(@host, @port)
37
- transport = Thrift::FramedTransport.new(socket)
38
- protocol = Thrift::BinaryProtocol.new(transport)
63
+ socket = if @tls
64
+ ssl_context = OpenSSL::SSL::SSLContext.new.tap do |ctx|
65
+ ctx.verify_mode = OpenSSL::SSL::VERIFY_PEER
66
+ ctx.min_version = OpenSSL::SSL::TLS1_2_VERSION
67
+
68
+ keys_dir = File.expand_path("../../../test/keys", __dir__)
69
+ ctx.ca_file = File.join(keys_dir, "CA.pem")
70
+ ctx.cert = OpenSSL::X509::Certificate.new(File.open(File.join(keys_dir, "client.crt")))
71
+ ctx.cert_store = OpenSSL::X509::Store.new
72
+ ctx.cert_store.add_file(File.join(keys_dir, 'server.pem'))
73
+ ctx.key = OpenSSL::PKey::RSA.new(File.open(File.join(keys_dir, "client.key")))
74
+ end
75
+
76
+ Thrift::SSLSocket.new(@host, @port, 5, ssl_context)
77
+ else
78
+ Thrift::Socket.new(@host, @port, 5)
79
+ end
80
+ protocol = create_protocol(socket)
81
+ transport = protocol.trans
39
82
  client = ThriftBenchmark::BenchmarkService::Client.new(protocol)
40
83
  begin
41
84
  start = Time.now
@@ -68,7 +111,8 @@ class Client
68
111
  end
69
112
 
70
113
  log_exceptions = true if ARGV[0] == '-log-exceptions' and ARGV.shift
114
+ tls = true if ARGV[0] == '-tls' and ARGV.shift
71
115
 
72
- host, port, clients_per_process, calls_per_client = ARGV
116
+ host, port, clients_per_process, calls_per_client, protocol_type = ARGV
73
117
 
74
- Client.new(host, port.to_i, clients_per_process.to_i, calls_per_client.to_i, log_exceptions).run
118
+ Client.new(host, port.to_i, clients_per_process.to_i, calls_per_client.to_i, log_exceptions, tls, protocol_type).run
data/benchmark/server.rb CHANGED
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  #
2
3
  # Licensed to the Apache Software Foundation (ASF) under one
3
4
  # or more contributor license agreements. See the NOTICE file
@@ -18,7 +19,9 @@
18
19
  #
19
20
 
20
21
  $:.unshift File.dirname(__FILE__) + '/../lib'
22
+ $:.unshift File.dirname(__FILE__) + '/../ext'
21
23
  require 'thrift'
24
+ require 'openssl'
22
25
  $:.unshift File.dirname(__FILE__) + "/gen-rb"
23
26
  require 'benchmark_service'
24
27
 
@@ -36,12 +39,46 @@ module Server
36
39
  end
37
40
  end
38
41
 
39
- def self.start_server(host, port, serverClass)
42
+ def self.create_factories(protocol_type)
43
+ case protocol_type
44
+ when 'binary'
45
+ [FramedTransportFactory.new, BinaryProtocolFactory.new]
46
+ when 'compact'
47
+ [FramedTransportFactory.new, CompactProtocolFactory.new]
48
+ when 'header'
49
+ [HeaderTransportFactory.new, HeaderProtocolFactory.new]
50
+ when 'header-compact'
51
+ [HeaderTransportFactory.new, HeaderProtocolFactory.new(nil, HeaderSubprotocolID::COMPACT)]
52
+ when 'header-zlib'
53
+ # Note: Server doesn't add transforms - it mirrors client's transforms
54
+ [HeaderTransportFactory.new, HeaderProtocolFactory.new]
55
+ else
56
+ [FramedTransportFactory.new, BinaryProtocolFactory.new]
57
+ end
58
+ end
59
+
60
+ def self.start_server(host, port, serverClass, tls, protocol_type = nil)
40
61
  handler = BenchmarkHandler.new
41
62
  processor = ThriftBenchmark::BenchmarkService::Processor.new(handler)
42
- transport = ServerSocket.new(host, port)
43
- transport_factory = FramedTransportFactory.new
44
- args = [processor, transport, transport_factory, nil, 20]
63
+ transport = if tls
64
+ ssl_context = OpenSSL::SSL::SSLContext.new.tap do |ctx|
65
+ ctx.verify_mode = OpenSSL::SSL::VERIFY_PEER
66
+ ctx.min_version = OpenSSL::SSL::TLS1_2_VERSION
67
+
68
+ keys_dir = File.expand_path("../../../test/keys", __dir__)
69
+ ctx.ca_file = File.join(keys_dir, "CA.pem")
70
+ ctx.cert = OpenSSL::X509::Certificate.new(File.open(File.join(keys_dir, "server.crt")))
71
+ ctx.cert_store = OpenSSL::X509::Store.new
72
+ ctx.cert_store.add_file(File.join(keys_dir, 'client.pem'))
73
+ ctx.key = OpenSSL::PKey::RSA.new(File.open(File.join(keys_dir, "server.key")))
74
+ end
75
+
76
+ Thrift::SSLServerSocket.new(host, port, ssl_context)
77
+ else
78
+ ServerSocket.new(host, port)
79
+ end
80
+ transport_factory, protocol_factory = create_factories(protocol_type || 'binary')
81
+ args = [processor, transport, transport_factory, protocol_factory, 20]
45
82
  if serverClass == NonblockingServer
46
83
  logger = Logger.new(STDERR)
47
84
  logger.level = Logger::WARN
@@ -65,12 +102,14 @@ module Server
65
102
  end
66
103
 
67
104
  def resolve_const(const)
68
- const and const.split('::').inject(Object) { |k,c| k.const_get(c) }
105
+ const and const.split('::').inject(Object) { |k, c| k.const_get(c) }
69
106
  end
70
107
 
71
- host, port, serverklass = ARGV
108
+ tls = true if ARGV[0] == '-tls' and ARGV.shift
109
+
110
+ host, port, serverklass, protocol_type = ARGV
72
111
 
73
- Server.start_server(host, port.to_i, resolve_const(serverklass))
112
+ Server.start_server(host, port.to_i, resolve_const(serverklass), tls, protocol_type)
74
113
 
75
114
  # let our host know that the interpreter has started
76
115
  # ideally we'd wait until the server was serving, but we don't have a hook for that
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  #
2
3
  # Licensed to the Apache Software Foundation (ASF) under one
3
4
  # or more contributor license agreements. See the NOTICE file
@@ -18,6 +19,7 @@
18
19
  #
19
20
 
20
21
  $:.unshift File.dirname(__FILE__) + '/../lib'
22
+ $:.unshift File.dirname(__FILE__) + '/../ext'
21
23
  require 'thrift'
22
24
  $:.unshift File.dirname(__FILE__) + "/gen-rb"
23
25
  require 'benchmark_service'