thrift 0.23.0 → 0.25.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 (120) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +203 -40
  3. data/ext/binary_protocol_accelerated.c +130 -40
  4. data/ext/bytes.c +14 -0
  5. data/ext/compact_protocol.c +184 -91
  6. data/ext/constants.h +3 -0
  7. data/ext/extconf.rb +12 -4
  8. data/ext/macros.h +11 -0
  9. data/ext/memory_buffer.c +67 -12
  10. data/ext/struct.c +192 -71
  11. data/ext/thrift_native.c +7 -0
  12. data/lib/thrift/bytes.rb +6 -3
  13. data/lib/thrift/client.rb +31 -33
  14. data/lib/thrift/exceptions.rb +11 -8
  15. data/lib/thrift/multiplexed_processor.rb +17 -8
  16. data/lib/thrift/processor.rb +25 -3
  17. data/lib/thrift/protocol/base_protocol.rb +66 -31
  18. data/lib/thrift/protocol/binary_protocol.rb +80 -37
  19. data/lib/thrift/protocol/binary_protocol_accelerated.rb +7 -2
  20. data/lib/thrift/protocol/compact_protocol.rb +204 -88
  21. data/lib/thrift/protocol/header_protocol.rb +15 -3
  22. data/lib/thrift/protocol/json_protocol.rb +158 -109
  23. data/lib/thrift/protocol/multiplexed_protocol.rb +3 -3
  24. data/lib/thrift/protocol/protocol_decorator.rb +6 -2
  25. data/lib/thrift/serializer/deserializer.rb +1 -0
  26. data/lib/thrift/serializer/serializer.rb +7 -0
  27. data/lib/thrift/server/base_server.rb +11 -2
  28. data/lib/thrift/server/nonblocking_server.rb +69 -20
  29. data/lib/thrift/server/rack_application.rb +69 -0
  30. data/lib/thrift/server/simple_server.rb +27 -15
  31. data/lib/thrift/server/thin_http_server.rb +21 -44
  32. data/lib/thrift/server/thread_pool_server.rb +21 -17
  33. data/lib/thrift/server/threaded_server.rb +22 -18
  34. data/lib/thrift/struct.rb +34 -18
  35. data/lib/thrift/struct_union.rb +37 -36
  36. data/lib/thrift/thrift_native.rb +2 -1
  37. data/lib/thrift/transport/base_server_transport.rb +3 -0
  38. data/lib/thrift/transport/base_transport.rb +26 -4
  39. data/lib/thrift/transport/buffered_transport.rb +5 -4
  40. data/lib/thrift/transport/framed_transport.rb +33 -7
  41. data/lib/thrift/transport/header_transport.rb +159 -33
  42. data/lib/thrift/transport/http_client_transport.rb +28 -14
  43. data/lib/thrift/transport/io_stream_transport.rb +3 -1
  44. data/lib/thrift/transport/memory_buffer_transport.rb +14 -9
  45. data/lib/thrift/transport/server_socket.rb +10 -8
  46. data/lib/thrift/transport/socket.rb +85 -48
  47. data/lib/thrift/transport/ssl_server_socket.rb +67 -6
  48. data/lib/thrift/transport/ssl_socket.rb +73 -14
  49. data/lib/thrift/transport/unix_server_socket.rb +6 -2
  50. data/lib/thrift/transport/unix_socket.rb +5 -6
  51. data/lib/thrift/types.rb +23 -22
  52. data/lib/thrift/union.rb +21 -11
  53. data/lib/thrift/uuid.rb +9 -8
  54. data/lib/thrift.rb +40 -39
  55. metadata +7 -266
  56. data/benchmark/Benchmark.thrift +0 -24
  57. data/benchmark/benchmark.rb +0 -285
  58. data/benchmark/client.rb +0 -117
  59. data/benchmark/server.rb +0 -120
  60. data/benchmark/thin_server.rb +0 -45
  61. data/lib/thrift/server/mongrel_http_server.rb +0 -60
  62. data/spec/BaseService.thrift +0 -27
  63. data/spec/ExtendedService.thrift +0 -25
  64. data/spec/Referenced.thrift +0 -44
  65. data/spec/ThriftNamespacedSpec.thrift +0 -53
  66. data/spec/ThriftSpec.thrift +0 -187
  67. data/spec/base_protocol_spec.rb +0 -224
  68. data/spec/base_transport_spec.rb +0 -387
  69. data/spec/binary_protocol_accelerated_spec.rb +0 -46
  70. data/spec/binary_protocol_spec.rb +0 -72
  71. data/spec/binary_protocol_spec_shared.rb +0 -445
  72. data/spec/bytes_spec.rb +0 -99
  73. data/spec/client_spec.rb +0 -166
  74. data/spec/compact_protocol_spec.rb +0 -196
  75. data/spec/constants_demo_spec.rb +0 -101
  76. data/spec/exception_spec.rb +0 -140
  77. data/spec/flat_spec.rb +0 -62
  78. data/spec/header_protocol_spec.rb +0 -475
  79. data/spec/header_transport_spec.rb +0 -386
  80. data/spec/http_client_spec.rb +0 -159
  81. data/spec/json_protocol_spec.rb +0 -552
  82. data/spec/namespaced_spec.rb +0 -66
  83. data/spec/nonblocking_server_spec.rb +0 -361
  84. data/spec/processor_spec.rb +0 -79
  85. data/spec/serializer_spec.rb +0 -66
  86. data/spec/server_socket_spec.rb +0 -84
  87. data/spec/server_spec.rb +0 -186
  88. data/spec/socket_spec.rb +0 -67
  89. data/spec/socket_spec_shared.rb +0 -167
  90. data/spec/spec_helper.rb +0 -69
  91. data/spec/ssl_server_socket_spec.rb +0 -45
  92. data/spec/ssl_socket_spec.rb +0 -87
  93. data/spec/struct_nested_containers_spec.rb +0 -190
  94. data/spec/struct_spec.rb +0 -397
  95. data/spec/support/header_protocol_helper.rb +0 -54
  96. data/spec/thin_http_server_spec.rb +0 -126
  97. data/spec/types_spec.rb +0 -117
  98. data/spec/union_spec.rb +0 -272
  99. data/spec/unix_socket_spec.rb +0 -115
  100. data/spec/uuid_validation_spec.rb +0 -238
  101. data/test/fuzz/Makefile.am +0 -173
  102. data/test/fuzz/README.md +0 -149
  103. data/test/fuzz/fuzz_common.rb +0 -95
  104. data/test/fuzz/fuzz_parse_binary_protocol.rb +0 -22
  105. data/test/fuzz/fuzz_parse_binary_protocol_accelerated.rb +0 -22
  106. data/test/fuzz/fuzz_parse_binary_protocol_accelerated_harness.rb +0 -22
  107. data/test/fuzz/fuzz_parse_binary_protocol_harness.rb +0 -22
  108. data/test/fuzz/fuzz_parse_compact_protocol.rb +0 -22
  109. data/test/fuzz/fuzz_parse_compact_protocol_harness.rb +0 -22
  110. data/test/fuzz/fuzz_parse_json_protocol.rb +0 -22
  111. data/test/fuzz/fuzz_parse_json_protocol_harness.rb +0 -22
  112. data/test/fuzz/fuzz_roundtrip_binary_protocol.rb +0 -22
  113. data/test/fuzz/fuzz_roundtrip_binary_protocol_accelerated.rb +0 -22
  114. data/test/fuzz/fuzz_roundtrip_binary_protocol_accelerated_harness.rb +0 -22
  115. data/test/fuzz/fuzz_roundtrip_binary_protocol_harness.rb +0 -22
  116. data/test/fuzz/fuzz_roundtrip_compact_protocol.rb +0 -22
  117. data/test/fuzz/fuzz_roundtrip_compact_protocol_harness.rb +0 -22
  118. data/test/fuzz/fuzz_roundtrip_json_protocol.rb +0 -22
  119. data/test/fuzz/fuzz_roundtrip_json_protocol_harness.rb +0 -22
  120. data/test/fuzz/fuzz_tracer.rb +0 -28
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 97c9f50f4cf15be35eb546b5762d7625e3c2b3f8b97765217962409ba66acb99
4
- data.tar.gz: 92ebb46420c41a9aaa1db0a78cbb7e513d43898c3064283a0b521d4037e63d22
3
+ metadata.gz: 0eacf1574da356dba55962561224cd683bf1c073e531b8402f469e91cb964dd8
4
+ data.tar.gz: b7f8aed51a240fab797e8137d56bd65c25d04291b292be40b3475831b945d4f5
5
5
  SHA512:
6
- metadata.gz: 4d5bcb6f79e7625989ff71e4693de7a0ba19babddb01c7727f4820343947af84ee0fef2a916d836c6a8343a4516670fae8af8f7a35bf8b3c94ab001000615baf
7
- data.tar.gz: 6216728eb0196480044992e42c8896f82d6e84314aa530bcc438ee50fa636d690a8bd9196868312cc70263ac3e53f99887001cd3073577573baf63f95142f504
6
+ metadata.gz: 35f22ce7f4d25a25c18611548eaf575a33a62bdd3a673876ac36fa783ccb44bd7b0066ebf2b1158ad6687aaf49182a9af2a9f6c1955addcf71e8c7738f615a66
7
+ data.tar.gz: 0edd8cdaccb60eb0082da2aed7169f46b167ef821d68ba126c22945738165d20bca3a505fb09969c8abb5dccd212ea23d296ffcc02c2b6e46a29d361e925efcf
data/README.md CHANGED
@@ -27,7 +27,7 @@ clients and services.
27
27
 
28
28
  ## Compatibility
29
29
 
30
- - Ruby MRI >= 2.7 (tested against current supported releases).
30
+ - Ruby MRI >= 3.0 (tested against current supported releases).
31
31
  - JRuby works with the pure-Ruby implementation; the native extension is
32
32
  skipped automatically.
33
33
  - For the repo-wide transport, protocol, and server support matrix, see
@@ -36,7 +36,7 @@ clients and services.
36
36
 
37
37
  ## Installation
38
38
 
39
- - Requirements: Ruby >= 2.7.
39
+ - Requirements: Ruby >= 3.0.
40
40
  - From RubyGems: `gem install thrift`
41
41
  - From source: `bundle install`, `gem build thrift.gemspec`, then install the
42
42
  resulting `thrift-*.gem`. The native accelerator is built when the gem is
@@ -47,49 +47,80 @@ clients and services.
47
47
  The Ruby library does not include the Thrift compiler. Use a compiler built
48
48
  from the root of this repository to generate Ruby bindings:
49
49
 
50
- thrift --gen rb path/to/service.thrift
51
- # with namespaced modules
52
- thrift --gen rb:namespaced --recurse path/to/service.thrift
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
+ ```
53
55
 
54
56
  Generated files are typically written to `gen-rb/` and can be required
55
57
  directly from your application.
56
58
 
57
59
  ## Basic Client Usage
58
60
 
59
- $:.push File.expand_path('gen-rb', __dir__)
60
- require 'thrift'
61
- require 'calculator'
61
+ ```ruby
62
+ $:.push File.expand_path('gen-rb', __dir__)
63
+ require 'thrift'
64
+ require 'calculator'
62
65
 
63
- socket = Thrift::Socket.new('localhost', 9090)
64
- transport = Thrift::BufferedTransport.new(socket)
65
- protocol = Thrift::BinaryProtocol.new(transport)
66
- client = Calculator::Client.new(protocol)
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)
67
70
 
68
- transport.open
69
- puts client.add(1, 1)
70
- transport.close
71
+ transport.open
72
+ puts client.add(1, 1)
73
+ transport.close
74
+ ```
71
75
 
72
76
  ## Basic Server Usage
73
77
 
74
- $:.push File.expand_path('gen-rb', __dir__)
75
- require 'thrift'
76
- require 'calculator'
77
-
78
- class CalculatorHandler
79
- def add(a, b)
80
- a + b
81
- end
82
- end
83
-
84
- handler = CalculatorHandler.new
85
- processor = Calculator::Processor.new(handler)
86
- server_transport = Thrift::ServerSocket.new(9090)
87
- transport_factory = Thrift::BufferedTransportFactory.new
88
- protocol_factory = Thrift::BinaryProtocolFactory.new
89
-
90
- server = Thrift::ThreadedServer.new(processor, server_transport,
91
- transport_factory, protocol_factory)
92
- server.serve
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
+ ## Rack HTTP Endpoint
101
+
102
+ Ruby HTTP transport can be mounted as a Rack application, so applications can
103
+ run Thrift on an existing Rack server such as Puma or Falcon.
104
+ The examples below use the generated `Calculator::Processor` and the
105
+ `CalculatorHandler` from [Basic Server Usage](#basic-server-usage).
106
+
107
+ ```ruby
108
+ # config.ru
109
+ require 'thrift'
110
+ require 'thrift/server/rack_application'
111
+
112
+ processor = Calculator::Processor.new(CalculatorHandler.new)
113
+ run Thrift::RackApplication.new(processor)
114
+ ```
115
+
116
+ For Rails or another Rack router, mount the endpoint at the route that should
117
+ receive Thrift HTTP requests:
118
+
119
+ ```ruby
120
+ # config/routes.rb
121
+ processor = Calculator::Processor.new(CalculatorHandler.new)
122
+ mount Thrift::RackApplication.new(processor) => "/thrift"
123
+ ```
93
124
 
94
125
  ## Development and Tests
95
126
 
@@ -111,6 +142,109 @@ directly from your application.
111
142
 
112
143
  ## Breaking Changes
113
144
 
145
+ ### 0.25.0
146
+
147
+ Ruby 2.7 support has been removed because the runtime reached end of life on
148
+ March 31, 2023. The Thrift gem now requires Ruby 3.0 or newer. Applications
149
+ still running Ruby 2.7 must upgrade Ruby before installing Thrift 0.25.0 or
150
+ remain on an earlier Thrift release.
151
+
152
+ Ruby HTTP servers now share one Rack endpoint. Run `Thrift::RackApplication`
153
+ as a Rack app, or mount it at one path in an app such as Rails. The
154
+ cross-language HTTP tests now cover Puma and Falcon.
155
+
156
+ `Thrift::MongrelHTTPServer` has been removed because Mongrel no longer works
157
+ with supported Ruby versions. To migrate, mount `Thrift::RackApplication` in
158
+ a supported Rack server. See [Rack HTTP Endpoint](#rack-http-endpoint).
159
+
160
+ `Thrift::ThinHTTPServer` is deprecated because Thin is no longer maintained.
161
+ Its EventMachine dependency also does not build on new Ruby development
162
+ versions. Creating a Thin server now prints a warning. Move to
163
+ `Thrift::RackApplication` with a supported Rack server such as Puma or Falcon.
164
+
165
+ `Thrift::SSLSocket` now verifies peers by default when no SSL context is
166
+ provided. It creates a context that uses the system certificate store. A
167
+ supplied SSL context is not reconfigured by Thrift, so the application is
168
+ responsible for its verification mode and trust sources. A newly constructed
169
+ `OpenSSL::SSL::SSLContext` defaults to `OpenSSL::SSL::VERIFY_NONE`; configuring
170
+ a trust source alone does not enable certificate chain verification. The server
171
+ certificate must still match the connection hostname.
172
+
173
+ `Thrift::NonblockingServer` now closes a connection whose frame declares more than
174
+ `max_frame_size` before it collects the frame. `max_frame_size` is a new keyword argument
175
+ and defaults to `Thrift::FramedTransport::DEFAULT_MAX_FRAME_SIZE` (16,384,000 bytes), the
176
+ limit `FramedTransport` already applies to the frame the worker reads back. A server that
177
+ accepted larger frames must pass a higher `max_frame_size:` to keep doing so.
178
+
179
+ `Thrift::Socket#open` now raises
180
+ `Thrift::TransportException::ALREADY_OPEN` when the TCP transport is already
181
+ open. Close the transport before opening it again.
182
+
183
+ Ruby struct, union, and exception serialization methods now accept an optional
184
+ remaining struct depth: `read(protocol, depth = 64)` and
185
+ `write(protocol, depth = 64)`.
186
+ Unknown and mismatched fields remain independently bounded by
187
+ `Thrift::BaseProtocol#skip`. The `write_field` and `write_type` helpers accept
188
+ an optional `remaining_depth`: custom writers must use
189
+ `write_field(field_info, fid, value, remaining_depth)` or
190
+ `write_type(field_info, value, remaining_depth)` for struct values. Custom
191
+ protocols and custom struct, union, or exception serialization overrides with
192
+ exact argument counts must accept and forward the optional depth argument (or
193
+ use `*args`) before upgrading.
194
+
195
+ `Thrift::BinaryProtocol`, `Thrift::BinaryProtocolAccelerated` and `Thrift::CompactProtocol`
196
+ now refuse a string or binary field longer than `max_string_size` before reading it,
197
+ with a `ProtocolException` of type `SIZE_LIMIT`. The maximum defaults to
198
+ `Thrift::BaseProtocol::DEFAULT_MAX_STRING_SIZE`, 16384000 bytes, the frame size limit
199
+ the framed transports apply, and is a keyword argument of the three protocols and
200
+ their factories. Pass `max_string_size: nil` to read strings of any length, as before.
201
+
202
+ ### 0.24.0
203
+
204
+ Connect timeout handling changed for both `Thrift::Socket` and
205
+ `Thrift::SSLSocket`.
206
+
207
+ - `timeout == nil` and `timeout == 0` now use blocking connect/open semantics.
208
+ Older releases already treated `nil` that way, but treated `0` differently
209
+ across operations: `read` and `write` used the blocking path, plain TCP
210
+ `open` used a zero-length poll, and TLS `open` could spin in the handshake
211
+ retry loop.
212
+ - Positive timeouts now bound the whole connect/open operation instead of
213
+ effectively applying a fresh timeout window at each wait or address attempt.
214
+ For plain TCP this budget is shared across address fallback. For TLS it is
215
+ shared across the TCP connect and the SSL handshake.
216
+ - Connect/open timeout expiry now raises
217
+ `Thrift::TransportException::TIMED_OUT`. Older releases could report the same
218
+ condition as `NOT_OPEN`, and `Thrift::SSLSocket` could keep retrying the
219
+ handshake after the wait timed out.
220
+
221
+ If your application matched `NOT_OPEN` for connect timeout handling, update it
222
+ to handle `TIMED_OUT`. If you relied on `timeout = 0` meaning immediate failure
223
+ or on repeated retries extending the effective timeout during TCP fallback or
224
+ TLS handshake, update those call paths before upgrading.
225
+
226
+ Ruby server socket transports now apply a 5-second timeout to accepted client
227
+ sockets by default. This prevents stalled clients from blocking server threads
228
+ indefinitely during response writes. Applications that intentionally require
229
+ blocking accepted sockets can pass `client_timeout: nil` or `client_timeout: 0`
230
+ when constructing `Thrift::ServerSocket`, `Thrift::SSLServerSocket`, or
231
+ `Thrift::UNIXServerSocket`.
232
+
233
+ Generated Ruby structs and unions now suffix field ID constants as
234
+ `*_FIELD_ID` instead of exposing bare uppercased field names. For example,
235
+ `MyStruct::FOO` becomes `MyStruct::FOO_FIELD_ID`. This avoids collisions with
236
+ the generated `FIELDS` metadata hash for field names such as `fields`, but it
237
+ is a source-compatible break if your application referenced the old constants
238
+ directly. Regenerate Ruby code and update those constant references atomically.
239
+
240
+ Ruby binary protocol writers now enforce signed Thrift integer ranges before
241
+ serializing values. `byte`, `i16`, `i32`, and `i64` writes reject values outside
242
+ their declared signed ranges, and binary/string/container sizes must fit in a
243
+ non-negative signed `i32` length. Older releases could silently wrap or clip
244
+ some out-of-range values, such as writing `255` as a byte that read back as
245
+ `-1`. This applies to both `Thrift::BinaryProtocol` and
246
+ `Thrift::BinaryProtocolAccelerated`.
247
+
114
248
  ### 0.23.0
115
249
 
116
250
  The documented source-build flow now effectively requires Ruby `2.7+`.
@@ -157,14 +291,30 @@ best-effort, not guaranteed.
157
291
  `thrift --gen rb` and namespaced output from `thrift --gen rb:namespaced`
158
292
  use different require paths, so switch them atomically with regenerated code.
159
293
 
160
- # --gen rb
161
- require 'calculator'
294
+ ```ruby
295
+ # --gen rb
296
+ require 'calculator'
162
297
 
163
- # --gen rb:namespaced
164
- require 'my_namespace/calculator'
298
+ # --gen rb:namespaced
299
+ require 'my_namespace/calculator'
300
+ ```
165
301
 
166
302
  ## Migration Notes
167
303
 
304
+ - If you upgrade to `0.25.0`, note that `Thrift::SSLSocket` now verifies peers
305
+ by default when it creates the SSL context. Supplied contexts are used
306
+ unchanged and must configure their own verification mode and trust sources.
307
+ A blank supplied context defaults to `OpenSSL::SSL::VERIFY_NONE`. Hostname
308
+ checking is performed for both supplied and default contexts.
309
+ - If you upgrade to `0.25.0`, close a `Thrift::Socket` before calling `open`
310
+ again. A duplicate open now raises
311
+ `Thrift::TransportException::ALREADY_OPEN`.
312
+ - If you upgrade to `0.24.0`, treat `timeout` on `Thrift::Socket` and
313
+ `Thrift::SSLSocket` as one budget for the whole open path. For
314
+ `Thrift::SSLSocket`, that includes both the TCP connect and the TLS
315
+ handshake.
316
+ - If you upgrade to `0.24.0`, handle connect/open timeout expiry as
317
+ `Thrift::TransportException::TIMED_OUT` instead of `NOT_OPEN`.
168
318
  - If you upgrade across the stricter reply-validation changes, regenerate all
169
319
  Ruby stubs and deploy them with the matching Ruby runtime. Do not mix old
170
320
  generated code, new generated code, and new runtime code on the same client
@@ -197,5 +347,18 @@ best-effort, not guaranteed.
197
347
  - Client and server must agree on transport and protocol choices. If you
198
348
  switch to SSL, HTTP, header transport, compact protocol, or namespaced
199
349
  generated code, update both ends together.
200
- - HTTPS client transport verifies peers by default, and `Thrift::SSLSocket`
201
- performs a hostname check against the host you pass in.
350
+ - HTTPS client transport and `Thrift::SSLSocket` verify peers by default.
351
+ When no SSL context is provided, `Thrift::SSLSocket` creates one with peer
352
+ verification and the system certificate store. Thrift passes a supplied
353
+ context to OpenSSL without reconfiguring it. `Thrift::SSLSocket` always
354
+ checks the certificate against `server_hostname` (or the connection host by
355
+ default). `OpenSSL::SSL::SSLContext.new` defaults to
356
+ `OpenSSL::SSL::VERIFY_NONE`, and setting `ca_file`, `ca_path`, or `cert_store`
357
+ does not enable verification by itself. Set `verify_mode` explicitly when
358
+ supplying a context. For example, to disable certificate chain verification:
359
+
360
+ ```ruby
361
+ context = OpenSSL::SSL::SSLContext.new
362
+ context.verify_mode = OpenSSL::SSL::VERIFY_NONE
363
+ socket = Thrift::SSLSocket.new(host, port, nil, context)
364
+ ```
@@ -31,13 +31,59 @@ VALUE rb_thrift_binary_proto_native_qmark(VALUE self) {
31
31
  return Qtrue;
32
32
  }
33
33
 
34
-
35
-
36
34
  static int VERSION_1;
37
35
  static int VERSION_MASK;
38
36
  static int TYPE_MASK;
39
37
  static ID rbuf_ivar_id;
40
38
 
39
+ static int64_t checked_integer_value(VALUE value) {
40
+ if (!RB_INTEGER_TYPE_P(value)) {
41
+ rb_raise(rb_eTypeError, "integer argument expected");
42
+ }
43
+
44
+ return NUM2LL(value);
45
+ }
46
+
47
+ static int64_t checked_integer_range(VALUE value, int64_t min, int64_t max) {
48
+ int64_t integer = checked_integer_value(value);
49
+
50
+ if (integer < min || integer > max) {
51
+ rb_raise(rb_eRangeError, "integer out of bounds");
52
+ }
53
+
54
+ return integer;
55
+ }
56
+
57
+ static int8_t checked_byte_value(VALUE value) {
58
+ return (int8_t)checked_integer_range(value, INT8_MIN, INT8_MAX);
59
+ }
60
+
61
+ static int16_t checked_i16_value(VALUE value) {
62
+ return (int16_t)checked_integer_range(value, INT16_MIN, INT16_MAX);
63
+ }
64
+
65
+ static int32_t checked_i32_value(VALUE value) {
66
+ return (int32_t)checked_integer_range(value, INT32_MIN, INT32_MAX);
67
+ }
68
+
69
+ static int32_t checked_size_value(VALUE value) {
70
+ return (int32_t)checked_integer_range(value, 0, INT32_MAX);
71
+ }
72
+
73
+ static int64_t checked_i64_value(VALUE value) {
74
+ return checked_integer_range(value, INT64_MIN, INT64_MAX);
75
+ }
76
+
77
+ static int32_t checked_string_length(VALUE str) {
78
+ long length = RSTRING_LEN(str);
79
+
80
+ if (length > INT32_MAX) {
81
+ rb_raise(rb_eRangeError, "string too long");
82
+ }
83
+
84
+ return (int32_t)length;
85
+ }
86
+
41
87
  static void write_byte_direct(VALUE trans, int8_t b) {
42
88
  WRITE(trans, (char*)&b, 1);
43
89
  }
@@ -83,7 +129,7 @@ static void write_string_direct(VALUE trans, VALUE str) {
83
129
  rb_raise(rb_eStandardError, "Value should be a string");
84
130
  }
85
131
  str = convert_to_utf8_byte_buffer(str);
86
- write_i32_direct(trans, (int32_t)RSTRING_LEN(str));
132
+ write_i32_direct(trans, checked_string_length(str));
87
133
  rb_funcall(trans, write_method_id, 1, str);
88
134
  }
89
135
 
@@ -124,13 +170,14 @@ VALUE rb_thrift_binary_proto_write_message_begin(VALUE self, VALUE name, VALUE t
124
170
  VALUE strict_write = GET_STRICT_WRITE(self);
125
171
 
126
172
  if (strict_write == Qtrue) {
127
- write_i32_direct(trans, VERSION_1 | FIX2INT(type));
173
+ int8_t message_type = checked_byte_value(type);
174
+ write_i32_direct(trans, VERSION_1 | message_type);
128
175
  write_string_direct(trans, name);
129
- write_i32_direct(trans, FIX2INT(seqid));
176
+ write_i32_direct(trans, checked_i32_value(seqid));
130
177
  } else {
131
178
  write_string_direct(trans, name);
132
- write_byte_direct(trans, FIX2INT(type));
133
- write_i32_direct(trans, FIX2INT(seqid));
179
+ write_byte_direct(trans, checked_byte_value(type));
180
+ write_i32_direct(trans, checked_i32_value(seqid));
134
181
  }
135
182
 
136
183
  return Qnil;
@@ -138,8 +185,8 @@ VALUE rb_thrift_binary_proto_write_message_begin(VALUE self, VALUE name, VALUE t
138
185
 
139
186
  VALUE rb_thrift_binary_proto_write_field_begin(VALUE self, VALUE name, VALUE type, VALUE id) {
140
187
  VALUE trans = GET_TRANSPORT(self);
141
- write_byte_direct(trans, FIX2INT(type));
142
- write_i16_direct(trans, FIX2INT(id));
188
+ write_byte_direct(trans, checked_byte_value(type));
189
+ write_i16_direct(trans, checked_i16_value(id));
143
190
 
144
191
  return Qnil;
145
192
  }
@@ -151,17 +198,17 @@ VALUE rb_thrift_binary_proto_write_field_stop(VALUE self) {
151
198
 
152
199
  VALUE rb_thrift_binary_proto_write_map_begin(VALUE self, VALUE ktype, VALUE vtype, VALUE size) {
153
200
  VALUE trans = GET_TRANSPORT(self);
154
- write_byte_direct(trans, FIX2INT(ktype));
155
- write_byte_direct(trans, FIX2INT(vtype));
156
- write_i32_direct(trans, FIX2INT(size));
201
+ write_byte_direct(trans, checked_byte_value(ktype));
202
+ write_byte_direct(trans, checked_byte_value(vtype));
203
+ write_i32_direct(trans, checked_size_value(size));
157
204
 
158
205
  return Qnil;
159
206
  }
160
207
 
161
208
  VALUE rb_thrift_binary_proto_write_list_begin(VALUE self, VALUE etype, VALUE size) {
162
209
  VALUE trans = GET_TRANSPORT(self);
163
- write_byte_direct(trans, FIX2INT(etype));
164
- write_i32_direct(trans, FIX2INT(size));
210
+ write_byte_direct(trans, checked_byte_value(etype));
211
+ write_i32_direct(trans, checked_size_value(size));
165
212
 
166
213
  return Qnil;
167
214
  }
@@ -178,25 +225,25 @@ VALUE rb_thrift_binary_proto_write_bool(VALUE self, VALUE b) {
178
225
 
179
226
  VALUE rb_thrift_binary_proto_write_byte(VALUE self, VALUE byte) {
180
227
  CHECK_NIL(byte);
181
- write_byte_direct(GET_TRANSPORT(self), NUM2INT(byte));
228
+ write_byte_direct(GET_TRANSPORT(self), checked_byte_value(byte));
182
229
  return Qnil;
183
230
  }
184
231
 
185
232
  VALUE rb_thrift_binary_proto_write_i16(VALUE self, VALUE i16) {
186
233
  CHECK_NIL(i16);
187
- write_i16_direct(GET_TRANSPORT(self), FIX2INT(i16));
234
+ write_i16_direct(GET_TRANSPORT(self), checked_i16_value(i16));
188
235
  return Qnil;
189
236
  }
190
237
 
191
238
  VALUE rb_thrift_binary_proto_write_i32(VALUE self, VALUE i32) {
192
239
  CHECK_NIL(i32);
193
- write_i32_direct(GET_TRANSPORT(self), NUM2INT(i32));
240
+ write_i32_direct(GET_TRANSPORT(self), checked_i32_value(i32));
194
241
  return Qnil;
195
242
  }
196
243
 
197
244
  VALUE rb_thrift_binary_proto_write_i64(VALUE self, VALUE i64) {
198
245
  CHECK_NIL(i64);
199
- write_i64_direct(GET_TRANSPORT(self), NUM2LL(i64));
246
+ write_i64_direct(GET_TRANSPORT(self), checked_i64_value(i64));
200
247
  return Qnil;
201
248
  }
202
249
 
@@ -224,7 +271,7 @@ VALUE rb_thrift_binary_proto_write_binary(VALUE self, VALUE buf) {
224
271
  CHECK_NIL(buf);
225
272
  VALUE trans = GET_TRANSPORT(self);
226
273
  buf = force_binary_encoding(buf);
227
- write_i32_direct(trans, (int32_t)RSTRING_LEN(buf));
274
+ write_i32_direct(trans, checked_string_length(buf));
228
275
  rb_funcall(trans, write_method_id, 1, buf);
229
276
  return Qnil;
230
277
  }
@@ -284,33 +331,62 @@ static char read_byte_direct(VALUE self) {
284
331
  return (char)(FIX2INT(byte));
285
332
  }
286
333
 
334
+ static int16_t uint16_to_int16(uint16_t value) {
335
+ if (value <= INT16_MAX) {
336
+ return (int16_t)value;
337
+ }
338
+ return (int16_t)((int32_t)value - ((int32_t)UINT16_MAX + 1));
339
+ }
340
+
341
+ static int32_t uint32_to_int32(uint32_t value) {
342
+ if (value <= INT32_MAX) {
343
+ return (int32_t)value;
344
+ }
345
+ return (int32_t)((int64_t)value - (INT64_C(1) << 32));
346
+ }
347
+
348
+ static int64_t uint64_to_int64(uint64_t value) {
349
+ if (value <= INT64_MAX) {
350
+ return (int64_t)value;
351
+ }
352
+ return -(int64_t)(~value) - INT64_C(1);
353
+ }
354
+
287
355
  static int16_t read_i16_direct(VALUE self) {
288
356
  VALUE rbuf = rb_ivar_get(self, rbuf_ivar_id);
289
357
  rb_funcall(GET_TRANSPORT(self), read_into_buffer_method_id, 2, rbuf, INT2FIX(2));
290
- return (int16_t)(((uint8_t)(RSTRING_PTR(rbuf)[1])) | ((uint16_t)((RSTRING_PTR(rbuf)[0]) << 8)));
358
+ const uint8_t* bytes = (const uint8_t*)RSTRING_PTR(rbuf);
359
+ uint16_t value = ((uint16_t)bytes[0] << 8) | (uint16_t)bytes[1];
360
+ return uint16_to_int16(value);
291
361
  }
292
362
 
293
363
  static int32_t read_i32_direct(VALUE self) {
294
364
  VALUE rbuf = rb_ivar_get(self, rbuf_ivar_id);
295
365
  rb_funcall(GET_TRANSPORT(self), read_into_buffer_method_id, 2, rbuf, INT2FIX(4));
296
- return ((uint8_t)(RSTRING_PTR(rbuf)[3])) |
297
- (((uint8_t)(RSTRING_PTR(rbuf)[2])) << 8) |
298
- (((uint8_t)(RSTRING_PTR(rbuf)[1])) << 16) |
299
- (((uint8_t)(RSTRING_PTR(rbuf)[0])) << 24);
366
+ const uint8_t* bytes = (const uint8_t*)RSTRING_PTR(rbuf);
367
+ uint32_t value = ((uint32_t)bytes[0] << 24) |
368
+ ((uint32_t)bytes[1] << 16) |
369
+ ((uint32_t)bytes[2] << 8) |
370
+ (uint32_t)bytes[3];
371
+ return uint32_to_int32(value);
300
372
  }
301
373
 
302
- static int64_t read_i64_direct(VALUE self) {
374
+ static uint64_t read_u64_direct(VALUE self) {
303
375
  VALUE rbuf = rb_ivar_get(self, rbuf_ivar_id);
304
376
  rb_funcall(GET_TRANSPORT(self), read_into_buffer_method_id, 2, rbuf, INT2FIX(8));
305
- uint64_t hi = ((uint8_t)(RSTRING_PTR(rbuf)[3])) |
306
- (((uint8_t)(RSTRING_PTR(rbuf)[2])) << 8) |
307
- (((uint8_t)(RSTRING_PTR(rbuf)[1])) << 16) |
308
- (((uint8_t)(RSTRING_PTR(rbuf)[0])) << 24);
309
- uint32_t lo = ((uint8_t)(RSTRING_PTR(rbuf)[7])) |
310
- (((uint8_t)(RSTRING_PTR(rbuf)[6])) << 8) |
311
- (((uint8_t)(RSTRING_PTR(rbuf)[5])) << 16) |
312
- (((uint8_t)(RSTRING_PTR(rbuf)[4])) << 24);
313
- return (hi << 32) | lo;
377
+ const uint8_t* bytes = (const uint8_t*)RSTRING_PTR(rbuf);
378
+ return ((uint64_t)bytes[0] << 56) |
379
+ ((uint64_t)bytes[1] << 48) |
380
+ ((uint64_t)bytes[2] << 40) |
381
+ ((uint64_t)bytes[3] << 32) |
382
+ ((uint64_t)bytes[4] << 24) |
383
+ ((uint64_t)bytes[5] << 16) |
384
+ ((uint64_t)bytes[6] << 8) |
385
+ (uint64_t)bytes[7];
386
+ }
387
+
388
+ static int64_t read_i64_direct(VALUE self) {
389
+ return uint64_to_int64(read_u64_direct(self));
314
390
  }
315
391
 
316
392
  VALUE rb_thrift_binary_proto_read_message_end(VALUE self) {
@@ -346,6 +422,10 @@ VALUE rb_thrift_binary_proto_read_message_begin(VALUE self) {
346
422
  VALUE name, seqid;
347
423
  int type;
348
424
 
425
+ if (RTEST(rb_ivar_get(self, reset_message_size_ivar_id))) {
426
+ rb_funcall(GET_TRANSPORT(self), reset_message_size_method_id, 0);
427
+ }
428
+
349
429
  int version = read_i32_direct(self);
350
430
 
351
431
  if (version < 0) {
@@ -359,6 +439,7 @@ VALUE rb_thrift_binary_proto_read_message_begin(VALUE self) {
359
439
  if (strict_read == Qtrue) {
360
440
  rb_exc_raise(get_protocol_exception(INT2FIX(PROTOERR_BAD_VERSION), rb_str_new2("No version identifier, old protocol client?")));
361
441
  }
442
+ CHECK_STRING_SIZE(self, version);
362
443
  name = READ(self, version);
363
444
  type = read_byte_direct(self);
364
445
  seqid = rb_thrift_binary_proto_read_i32(self);
@@ -377,17 +458,24 @@ VALUE rb_thrift_binary_proto_read_field_begin(VALUE self) {
377
458
  }
378
459
  }
379
460
 
461
+ #define CHECK_NEGATIVE_SIZE(size) \
462
+ if (RB_UNLIKELY((size) < 0)) { \
463
+ rb_exc_raise(get_protocol_exception(INT2FIX(PROTOERR_NEGATIVE_SIZE), rb_str_new2("Negative size"))); \
464
+ }
465
+
380
466
  VALUE rb_thrift_binary_proto_read_map_begin(VALUE self) {
381
467
  VALUE ktype = rb_thrift_binary_proto_read_byte(self);
382
468
  VALUE vtype = rb_thrift_binary_proto_read_byte(self);
383
- VALUE size = rb_thrift_binary_proto_read_i32(self);
384
- return rb_ary_new3(3, ktype, vtype, size);
469
+ int size = read_i32_direct(self);
470
+ CHECK_NEGATIVE_SIZE(size);
471
+ return rb_ary_new3(3, ktype, vtype, INT2NUM(size));
385
472
  }
386
473
 
387
474
  VALUE rb_thrift_binary_proto_read_list_begin(VALUE self) {
388
475
  VALUE etype = rb_thrift_binary_proto_read_byte(self);
389
- VALUE size = rb_thrift_binary_proto_read_i32(self);
390
- return rb_ary_new3(2, etype, size);
476
+ int size = read_i32_direct(self);
477
+ CHECK_NEGATIVE_SIZE(size);
478
+ return rb_ary_new3(2, etype, INT2NUM(size));
391
479
  }
392
480
 
393
481
  VALUE rb_thrift_binary_proto_read_set_begin(VALUE self) {
@@ -418,9 +506,9 @@ VALUE rb_thrift_binary_proto_read_i64(VALUE self) {
418
506
  VALUE rb_thrift_binary_proto_read_double(VALUE self) {
419
507
  union {
420
508
  double f;
421
- int64_t t;
509
+ uint64_t t;
422
510
  } transfer;
423
- transfer.t = read_i64_direct(self);
511
+ transfer.t = read_u64_direct(self);
424
512
  return rb_float_new(transfer.f);
425
513
  }
426
514
 
@@ -431,6 +519,8 @@ VALUE rb_thrift_binary_proto_read_string(VALUE self) {
431
519
 
432
520
  VALUE rb_thrift_binary_proto_read_binary(VALUE self) {
433
521
  int size = read_i32_direct(self);
522
+ CHECK_NEGATIVE_SIZE(size);
523
+ CHECK_STRING_SIZE(self, size);
434
524
  return READ(self, size);
435
525
  }
436
526
 
data/ext/bytes.c CHANGED
@@ -24,6 +24,20 @@
24
24
  #include <constants.h>
25
25
 
26
26
  VALUE force_binary_encoding(VALUE buffer) {
27
+ if (RB_TYPE_P(buffer, T_STRING)) {
28
+ if (rb_enc_get_index(buffer) == rb_ascii8bit_encindex()) {
29
+ return buffer;
30
+ }
31
+
32
+ if (RB_OBJ_FROZEN(buffer)) {
33
+ buffer = rb_obj_dup(buffer);
34
+ }
35
+
36
+ rb_enc_associate_index(buffer, rb_ascii8bit_encindex());
37
+
38
+ return buffer;
39
+ }
40
+
27
41
  return rb_funcall(thrift_bytes_module, force_binary_encoding_id, 1, buffer);
28
42
  }
29
43