thrift 0.24.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 +107 -4
  3. data/ext/binary_protocol_accelerated.c +52 -17
  4. data/ext/compact_protocol.c +135 -70
  5. data/ext/constants.h +3 -0
  6. data/ext/extconf.rb +4 -4
  7. data/ext/macros.h +11 -0
  8. data/ext/memory_buffer.c +35 -17
  9. data/ext/struct.c +185 -64
  10. data/ext/thrift_native.c +6 -0
  11. data/lib/thrift/bytes.rb +2 -2
  12. data/lib/thrift/client.rb +31 -22
  13. data/lib/thrift/exceptions.rb +10 -8
  14. data/lib/thrift/multiplexed_processor.rb +5 -6
  15. data/lib/thrift/processor.rb +24 -3
  16. data/lib/thrift/protocol/base_protocol.rb +53 -27
  17. data/lib/thrift/protocol/binary_protocol.rb +54 -40
  18. data/lib/thrift/protocol/binary_protocol_accelerated.rb +6 -2
  19. data/lib/thrift/protocol/compact_protocol.rb +193 -88
  20. data/lib/thrift/protocol/header_protocol.rb +14 -3
  21. data/lib/thrift/protocol/json_protocol.rb +146 -104
  22. data/lib/thrift/protocol/multiplexed_protocol.rb +2 -3
  23. data/lib/thrift/protocol/protocol_decorator.rb +5 -2
  24. data/lib/thrift/serializer/serializer.rb +6 -0
  25. data/lib/thrift/server/base_server.rb +10 -2
  26. data/lib/thrift/server/nonblocking_server.rb +49 -26
  27. data/lib/thrift/server/rack_application.rb +69 -0
  28. data/lib/thrift/server/simple_server.rb +27 -23
  29. data/lib/thrift/server/thin_http_server.rb +20 -44
  30. data/lib/thrift/server/thread_pool_server.rb +20 -17
  31. data/lib/thrift/server/threaded_server.rb +22 -26
  32. data/lib/thrift/struct.rb +33 -18
  33. data/lib/thrift/struct_union.rb +36 -30
  34. data/lib/thrift/transport/base_transport.rb +23 -5
  35. data/lib/thrift/transport/buffered_transport.rb +4 -4
  36. data/lib/thrift/transport/framed_transport.rb +32 -7
  37. data/lib/thrift/transport/header_transport.rb +130 -50
  38. data/lib/thrift/transport/http_client_transport.rb +27 -14
  39. data/lib/thrift/transport/io_stream_transport.rb +2 -1
  40. data/lib/thrift/transport/memory_buffer_transport.rb +8 -10
  41. data/lib/thrift/transport/server_socket.rb +4 -5
  42. data/lib/thrift/transport/socket.rb +20 -22
  43. data/lib/thrift/transport/ssl_server_socket.rb +64 -4
  44. data/lib/thrift/transport/ssl_socket.rb +32 -5
  45. data/lib/thrift/transport/unix_server_socket.rb +1 -1
  46. data/lib/thrift/transport/unix_socket.rb +4 -6
  47. data/lib/thrift/types.rb +22 -22
  48. data/lib/thrift/union.rb +20 -11
  49. data/lib/thrift/uuid.rb +8 -8
  50. data/lib/thrift.rb +39 -39
  51. metadata +5 -230
  52. data/benchmark/Benchmark.thrift +0 -24
  53. data/benchmark/benchmark.rb +0 -286
  54. data/benchmark/client.rb +0 -118
  55. data/benchmark/server.rb +0 -121
  56. data/benchmark/thin_server.rb +0 -46
  57. data/lib/thrift/server/mongrel_http_server.rb +0 -61
  58. data/spec/BaseService.thrift +0 -27
  59. data/spec/ExtendedService.thrift +0 -25
  60. data/spec/Referenced.thrift +0 -44
  61. data/spec/ThriftNamespacedSpec.thrift +0 -53
  62. data/spec/ThriftSpec.thrift +0 -203
  63. data/spec/base_protocol_spec.rb +0 -244
  64. data/spec/base_transport_spec.rb +0 -428
  65. data/spec/binary_protocol_accelerated_spec.rb +0 -47
  66. data/spec/binary_protocol_spec.rb +0 -73
  67. data/spec/binary_protocol_spec_shared.rb +0 -509
  68. data/spec/bytes_spec.rb +0 -117
  69. data/spec/client_spec.rb +0 -167
  70. data/spec/compact_protocol_spec.rb +0 -295
  71. data/spec/constants_demo_spec.rb +0 -102
  72. data/spec/exception_spec.rb +0 -141
  73. data/spec/flat_spec.rb +0 -63
  74. data/spec/header_protocol_spec.rb +0 -476
  75. data/spec/header_transport_spec.rb +0 -431
  76. data/spec/http_client_spec.rb +0 -160
  77. data/spec/json_protocol_spec.rb +0 -574
  78. data/spec/multiplexed_processor_spec.rb +0 -75
  79. data/spec/namespaced_spec.rb +0 -67
  80. data/spec/nonblocking_server_spec.rb +0 -429
  81. data/spec/processor_spec.rb +0 -80
  82. data/spec/recursion_depth_spec.rb +0 -223
  83. data/spec/serializer_spec.rb +0 -67
  84. data/spec/server_socket_spec.rb +0 -121
  85. data/spec/server_spec.rb +0 -238
  86. data/spec/socket_spec.rb +0 -174
  87. data/spec/socket_spec_shared.rb +0 -168
  88. data/spec/spec_helper.rb +0 -70
  89. data/spec/ssl_server_socket_spec.rb +0 -85
  90. data/spec/ssl_socket_spec.rb +0 -248
  91. data/spec/struct_nested_containers_spec.rb +0 -191
  92. data/spec/struct_spec.rb +0 -398
  93. data/spec/support/header_protocol_helper.rb +0 -55
  94. data/spec/thin_http_server_spec.rb +0 -127
  95. data/spec/types_spec.rb +0 -118
  96. data/spec/union_spec.rb +0 -273
  97. data/spec/unix_socket_spec.rb +0 -131
  98. data/spec/uuid_validation_spec.rb +0 -239
  99. data/test/fuzz/Makefile +0 -779
  100. data/test/fuzz/Makefile.am +0 -173
  101. data/test/fuzz/Makefile.in +0 -775
  102. data/test/fuzz/README.md +0 -149
  103. data/test/fuzz/fuzz_common.rb +0 -96
  104. data/test/fuzz/fuzz_parse_binary_protocol.rb +0 -23
  105. data/test/fuzz/fuzz_parse_binary_protocol_accelerated.rb +0 -23
  106. data/test/fuzz/fuzz_parse_binary_protocol_accelerated_harness.rb +0 -23
  107. data/test/fuzz/fuzz_parse_binary_protocol_harness.rb +0 -23
  108. data/test/fuzz/fuzz_parse_compact_protocol.rb +0 -23
  109. data/test/fuzz/fuzz_parse_compact_protocol_harness.rb +0 -23
  110. data/test/fuzz/fuzz_parse_json_protocol.rb +0 -23
  111. data/test/fuzz/fuzz_parse_json_protocol_harness.rb +0 -23
  112. data/test/fuzz/fuzz_roundtrip_binary_protocol.rb +0 -23
  113. data/test/fuzz/fuzz_roundtrip_binary_protocol_accelerated.rb +0 -23
  114. data/test/fuzz/fuzz_roundtrip_binary_protocol_accelerated_harness.rb +0 -23
  115. data/test/fuzz/fuzz_roundtrip_binary_protocol_harness.rb +0 -23
  116. data/test/fuzz/fuzz_roundtrip_compact_protocol.rb +0 -23
  117. data/test/fuzz/fuzz_roundtrip_compact_protocol_harness.rb +0 -23
  118. data/test/fuzz/fuzz_roundtrip_json_protocol.rb +0 -23
  119. data/test/fuzz/fuzz_roundtrip_json_protocol_harness.rb +0 -23
  120. data/test/fuzz/fuzz_tracer.rb +0 -29
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 90306a751b8d883922c4556778391d56c8c48fbf008187d3edfe411db4e9f6f7
4
- data.tar.gz: 95287ba8714521389775a7d2ad265b3ad60a3ca44ada102abcf0c2ee152347cd
3
+ metadata.gz: 0eacf1574da356dba55962561224cd683bf1c073e531b8402f469e91cb964dd8
4
+ data.tar.gz: b7f8aed51a240fab797e8137d56bd65c25d04291b292be40b3475831b945d4f5
5
5
  SHA512:
6
- metadata.gz: d7e052575e5b7e581440a9073d2f4c6ce828b79963f7ce5c077f77e7c759b3589d5671a9e6f37ff4df996061af6b77362cc6248c7d22ac414156594b3fd77339
7
- data.tar.gz: 35f5ee1d30099e5c70fa49d420153442e2b0b97587a908dea86ad1f9a2bbc44df5ff1baa3474cbc48f4064e20571eab7aa00c5fb155202f05edcd17579cbaddf
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
@@ -97,6 +97,31 @@ server = Thrift::ThreadedServer.new(processor, server_transport,
97
97
  server.serve
98
98
  ```
99
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
+ ```
124
+
100
125
  ## Development and Tests
101
126
 
102
127
  - `bundle exec rake spec` runs the Ruby specs. It expects a built Thrift
@@ -117,6 +142,63 @@ server.serve
117
142
 
118
143
  ## Breaking Changes
119
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
+
120
202
  ### 0.24.0
121
203
 
122
204
  Connect timeout handling changed for both `Thrift::Socket` and
@@ -219,6 +301,14 @@ best-effort, not guaranteed.
219
301
 
220
302
  ## Migration Notes
221
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`.
222
312
  - If you upgrade to `0.24.0`, treat `timeout` on `Thrift::Socket` and
223
313
  `Thrift::SSLSocket` as one budget for the whole open path. For
224
314
  `Thrift::SSLSocket`, that includes both the TCP connect and the TLS
@@ -257,5 +347,18 @@ best-effort, not guaranteed.
257
347
  - Client and server must agree on transport and protocol choices. If you
258
348
  switch to SSL, HTTP, header transport, compact protocol, or namespaced
259
349
  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.
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
+ ```
@@ -331,33 +331,62 @@ static char read_byte_direct(VALUE self) {
331
331
  return (char)(FIX2INT(byte));
332
332
  }
333
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
+
334
355
  static int16_t read_i16_direct(VALUE self) {
335
356
  VALUE rbuf = rb_ivar_get(self, rbuf_ivar_id);
336
357
  rb_funcall(GET_TRANSPORT(self), read_into_buffer_method_id, 2, rbuf, INT2FIX(2));
337
- 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);
338
361
  }
339
362
 
340
363
  static int32_t read_i32_direct(VALUE self) {
341
364
  VALUE rbuf = rb_ivar_get(self, rbuf_ivar_id);
342
365
  rb_funcall(GET_TRANSPORT(self), read_into_buffer_method_id, 2, rbuf, INT2FIX(4));
343
- return ((uint8_t)(RSTRING_PTR(rbuf)[3])) |
344
- (((uint8_t)(RSTRING_PTR(rbuf)[2])) << 8) |
345
- (((uint8_t)(RSTRING_PTR(rbuf)[1])) << 16) |
346
- (((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);
347
372
  }
348
373
 
349
- static int64_t read_i64_direct(VALUE self) {
374
+ static uint64_t read_u64_direct(VALUE self) {
350
375
  VALUE rbuf = rb_ivar_get(self, rbuf_ivar_id);
351
376
  rb_funcall(GET_TRANSPORT(self), read_into_buffer_method_id, 2, rbuf, INT2FIX(8));
352
- uint64_t hi = ((uint8_t)(RSTRING_PTR(rbuf)[3])) |
353
- (((uint8_t)(RSTRING_PTR(rbuf)[2])) << 8) |
354
- (((uint8_t)(RSTRING_PTR(rbuf)[1])) << 16) |
355
- (((uint8_t)(RSTRING_PTR(rbuf)[0])) << 24);
356
- uint32_t lo = ((uint8_t)(RSTRING_PTR(rbuf)[7])) |
357
- (((uint8_t)(RSTRING_PTR(rbuf)[6])) << 8) |
358
- (((uint8_t)(RSTRING_PTR(rbuf)[5])) << 16) |
359
- (((uint8_t)(RSTRING_PTR(rbuf)[4])) << 24);
360
- 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));
361
390
  }
362
391
 
363
392
  VALUE rb_thrift_binary_proto_read_message_end(VALUE self) {
@@ -393,6 +422,10 @@ VALUE rb_thrift_binary_proto_read_message_begin(VALUE self) {
393
422
  VALUE name, seqid;
394
423
  int type;
395
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
+
396
429
  int version = read_i32_direct(self);
397
430
 
398
431
  if (version < 0) {
@@ -406,6 +439,7 @@ VALUE rb_thrift_binary_proto_read_message_begin(VALUE self) {
406
439
  if (strict_read == Qtrue) {
407
440
  rb_exc_raise(get_protocol_exception(INT2FIX(PROTOERR_BAD_VERSION), rb_str_new2("No version identifier, old protocol client?")));
408
441
  }
442
+ CHECK_STRING_SIZE(self, version);
409
443
  name = READ(self, version);
410
444
  type = read_byte_direct(self);
411
445
  seqid = rb_thrift_binary_proto_read_i32(self);
@@ -472,9 +506,9 @@ VALUE rb_thrift_binary_proto_read_i64(VALUE self) {
472
506
  VALUE rb_thrift_binary_proto_read_double(VALUE self) {
473
507
  union {
474
508
  double f;
475
- int64_t t;
509
+ uint64_t t;
476
510
  } transfer;
477
- transfer.t = read_i64_direct(self);
511
+ transfer.t = read_u64_direct(self);
478
512
  return rb_float_new(transfer.f);
479
513
  }
480
514
 
@@ -486,6 +520,7 @@ VALUE rb_thrift_binary_proto_read_string(VALUE self) {
486
520
  VALUE rb_thrift_binary_proto_read_binary(VALUE self) {
487
521
  int size = read_i32_direct(self);
488
522
  CHECK_NEGATIVE_SIZE(size);
523
+ CHECK_STRING_SIZE(self, size);
489
524
  return READ(self, size);
490
525
  }
491
526
 
@@ -62,9 +62,43 @@ static int CTYPE_MAP = 0x0B;
62
62
  static int CTYPE_STRUCT = 0x0C;
63
63
  static int CTYPE_UUID = 0x0D;
64
64
 
65
- VALUE rb_thrift_compact_proto_write_i16(VALUE self, VALUE i16);
65
+ static uint32_t int_to_zig_zag(int32_t n);
66
+ static void write_varint32(VALUE transport, uint32_t n);
67
+
68
+ static int64_t checked_integer_range(VALUE value, int64_t min, int64_t max) {
69
+ CHECK_NIL(value);
70
+ if (RB_UNLIKELY(!RB_INTEGER_TYPE_P(value))) {
71
+ rb_raise(rb_eTypeError, "integer argument expected");
72
+ }
73
+
74
+ int64_t integer = NUM2LL(value);
75
+ if (RB_UNLIKELY(integer < min || integer > max)) {
76
+ rb_raise(rb_eRangeError, "integer out of bounds");
77
+ }
78
+
79
+ return integer;
80
+ }
81
+
82
+ static int8_t checked_byte_value(VALUE value) {
83
+ return (int8_t)checked_integer_range(value, INT8_MIN, INT8_MAX);
84
+ }
85
+
86
+ static int16_t checked_i16_value(VALUE value) {
87
+ return (int16_t)checked_integer_range(value, INT16_MIN, INT16_MAX);
88
+ }
89
+
90
+ static int32_t checked_i32_value(VALUE value) {
91
+ return (int32_t)checked_integer_range(value, INT32_MIN, INT32_MAX);
92
+ }
93
+
94
+ static int32_t checked_size_value(VALUE value) {
95
+ return (int32_t)checked_integer_range(value, 0, INT32_MAX);
96
+ }
97
+
98
+ static int64_t checked_i64_value(VALUE value) {
99
+ return checked_integer_range(value, INT64_MIN, INT64_MAX);
100
+ }
66
101
 
67
- // TODO: implement this
68
102
  static int get_compact_type(VALUE type_value) {
69
103
  int type = FIX2INT(type_value);
70
104
  if (type == TTYPE_BOOL) {
@@ -92,10 +126,11 @@ static int get_compact_type(VALUE type_value) {
92
126
  } else if (type == TTYPE_UUID) {
93
127
  return CTYPE_UUID;
94
128
  } else {
95
- char str[50];
96
- sprintf(str, "don't know what type: %d", type);
97
- rb_raise(rb_eStandardError, "%s", str);
98
- return 0;
129
+ rb_exc_raise(get_protocol_exception(
130
+ INT2FIX(PROTOERR_INVALID_DATA),
131
+ rb_sprintf("Unknown compact type: %d", type)
132
+ ));
133
+ return 0; /* unreachable */
99
134
  }
100
135
  }
101
136
 
@@ -103,13 +138,11 @@ static void write_byte_direct(VALUE transport, int8_t b) {
103
138
  WRITE(transport, (char*)&b, 1);
104
139
  }
105
140
 
106
- static void write_field_begin_internal(VALUE self, VALUE type, VALUE id_value, VALUE type_override) {
107
- int id = FIX2INT(id_value);
141
+ static void write_field_begin_internal(VALUE self, VALUE type, int16_t id, int8_t type_override) {
142
+ int8_t type_to_write = type_override == 0 ? get_compact_type(type) : type_override;
108
143
  int last_id = LAST_ID(self);
109
144
  VALUE transport = GET_TRANSPORT(self);
110
145
 
111
- // if there's a type override, use that.
112
- int8_t type_to_write = RTEST(type_override) ? FIX2INT(type_override) : get_compact_type(type);
113
146
  // check if we can use delta encoding for the field id
114
147
  int diff = id - last_id;
115
148
  if (diff > 0 && diff <= 15) {
@@ -118,10 +151,10 @@ static void write_field_begin_internal(VALUE self, VALUE type, VALUE id_value, V
118
151
  } else {
119
152
  // write them separate
120
153
  write_byte_direct(transport, type_to_write & 0x0f);
121
- rb_thrift_compact_proto_write_i16(self, id_value);
154
+ write_varint32(transport, int_to_zig_zag(id));
122
155
  }
123
156
 
124
- SET_LAST_ID(self, id_value);
157
+ SET_LAST_ID(self, INT2FIX(id));
125
158
  }
126
159
 
127
160
  static uint32_t int_to_zig_zag(int32_t n) {
@@ -141,35 +174,38 @@ static int32_t message_seqid_from_varint32(uint32_t seqid) {
141
174
  }
142
175
 
143
176
  static void write_varint32(VALUE transport, uint32_t n) {
144
- while (true) {
145
- if ((n & ~0x7FU) == 0U) {
146
- write_byte_direct(transport, n & 0x7FU);
147
- break;
148
- } else {
149
- write_byte_direct(transport, (n & 0x7FU) | 0x80U);
150
- n = n >> 7;
151
- }
177
+ unsigned char bytes[5];
178
+ long length = 0;
179
+
180
+ while ((n & ~0x7FU) != 0U) {
181
+ bytes[length++] = (n & 0x7FU) | 0x80U;
182
+ n >>= 7;
152
183
  }
184
+ bytes[length++] = n;
185
+
186
+ WRITE(transport, (const char*)bytes, length);
153
187
  }
154
188
 
155
189
  static void write_varint64(VALUE transport, uint64_t n) {
156
- while (true) {
157
- if ((n & ~0x7FULL) == 0ULL) {
158
- write_byte_direct(transport, n & 0x7FULL);
159
- break;
160
- } else {
161
- write_byte_direct(transport, (n & 0x7FULL) | 0x80ULL);
162
- n = n >> 7;
163
- }
190
+ unsigned char bytes[10];
191
+ long length = 0;
192
+
193
+ while ((n & ~0x7FULL) != 0ULL) {
194
+ bytes[length++] = (n & 0x7FULL) | 0x80ULL;
195
+ n >>= 7;
164
196
  }
197
+ bytes[length++] = n;
198
+
199
+ WRITE(transport, (const char*)bytes, length);
165
200
  }
166
201
 
167
202
  static void write_collection_begin(VALUE transport, VALUE elem_type, VALUE size_value) {
168
- int size = FIX2INT(size_value);
203
+ int size = checked_size_value(size_value);
204
+ int compact_type = get_compact_type(elem_type);
169
205
  if (size <= 14) {
170
- write_byte_direct(transport, size << 4 | get_compact_type(elem_type));
206
+ write_byte_direct(transport, size << 4 | compact_type);
171
207
  } else {
172
- write_byte_direct(transport, 0xf0 | get_compact_type(elem_type));
208
+ write_byte_direct(transport, 0xf0 | compact_type);
173
209
  write_varint32(transport, size);
174
210
  }
175
211
  }
@@ -216,9 +252,10 @@ VALUE rb_thrift_compact_proto_write_set_end(VALUE self) {
216
252
 
217
253
  VALUE rb_thrift_compact_proto_write_message_begin(VALUE self, VALUE name, VALUE type, VALUE seqid) {
218
254
  VALUE transport = GET_TRANSPORT(self);
219
- int32_t seqid_value = FIX2INT(seqid);
255
+ uint32_t type_value = (uint32_t)FIX2INT(type);
256
+ int32_t seqid_value = checked_i32_value(seqid);
220
257
  write_byte_direct(transport, PROTOCOL_ID);
221
- write_byte_direct(transport, (VERSION & VERSION_MASK) | ((FIX2INT(type) << TYPE_SHIFT_AMOUNT) & TYPE_MASK));
258
+ write_byte_direct(transport, (VERSION & VERSION_MASK) | ((type_value << TYPE_SHIFT_AMOUNT) & TYPE_MASK));
222
259
  write_varint32(transport, message_seqid_to_varint32(seqid_value));
223
260
  rb_thrift_compact_proto_write_string(self, name);
224
261
 
@@ -226,11 +263,12 @@ VALUE rb_thrift_compact_proto_write_message_begin(VALUE self, VALUE name, VALUE
226
263
  }
227
264
 
228
265
  VALUE rb_thrift_compact_proto_write_field_begin(VALUE self, VALUE name, VALUE type, VALUE id) {
266
+ int16_t field_id = checked_i16_value(id);
229
267
  if (FIX2INT(type) == TTYPE_BOOL) {
230
268
  // we want to possibly include the value, so we'll wait.
231
- rb_ivar_set(self, boolean_field_id, rb_ary_new3(2, type, id));
269
+ rb_ivar_set(self, boolean_field_id, rb_ary_new3(2, type, INT2FIX(field_id)));
232
270
  } else {
233
- write_field_begin_internal(self, type, id, Qnil);
271
+ write_field_begin_internal(self, type, field_id, 0);
234
272
  }
235
273
 
236
274
  return Qnil;
@@ -242,7 +280,7 @@ VALUE rb_thrift_compact_proto_write_field_stop(VALUE self) {
242
280
  }
243
281
 
244
282
  VALUE rb_thrift_compact_proto_write_map_begin(VALUE self, VALUE ktype, VALUE vtype, VALUE size_value) {
245
- int size = FIX2INT(size_value);
283
+ int size = checked_size_value(size_value);
246
284
  VALUE transport = GET_TRANSPORT(self);
247
285
  if (size == 0) {
248
286
  write_byte_direct(transport, 0);
@@ -264,39 +302,36 @@ VALUE rb_thrift_compact_proto_write_set_begin(VALUE self, VALUE etype, VALUE siz
264
302
  }
265
303
 
266
304
  VALUE rb_thrift_compact_proto_write_bool(VALUE self, VALUE b) {
267
- int8_t type = b == Qtrue ? CTYPE_BOOLEAN_TRUE : CTYPE_BOOLEAN_FALSE;
305
+ int8_t type = RTEST(b) ? CTYPE_BOOLEAN_TRUE : CTYPE_BOOLEAN_FALSE;
268
306
  VALUE boolean_field = rb_ivar_get(self, boolean_field_id);
269
307
  if (NIL_P(boolean_field)) {
270
308
  // we're not part of a field, so just write the value.
271
309
  write_byte_direct(GET_TRANSPORT(self), type);
272
310
  } else {
273
311
  // we haven't written the field header yet
274
- write_field_begin_internal(self, rb_ary_entry(boolean_field, 0), rb_ary_entry(boolean_field, 1), INT2FIX(type));
312
+ write_field_begin_internal(self, rb_ary_entry(boolean_field, 0), FIX2INT(rb_ary_entry(boolean_field, 1)), type);
275
313
  rb_ivar_set(self, boolean_field_id, Qnil);
276
314
  }
277
315
  return Qnil;
278
316
  }
279
317
 
280
318
  VALUE rb_thrift_compact_proto_write_byte(VALUE self, VALUE byte) {
281
- CHECK_NIL(byte);
282
- write_byte_direct(GET_TRANSPORT(self), FIX2INT(byte));
319
+ write_byte_direct(GET_TRANSPORT(self), checked_byte_value(byte));
283
320
  return Qnil;
284
321
  }
285
322
 
286
323
  VALUE rb_thrift_compact_proto_write_i16(VALUE self, VALUE i16) {
287
- rb_thrift_compact_proto_write_i32(self, i16);
324
+ write_varint32(GET_TRANSPORT(self), int_to_zig_zag(checked_i16_value(i16)));
288
325
  return Qnil;
289
326
  }
290
327
 
291
328
  VALUE rb_thrift_compact_proto_write_i32(VALUE self, VALUE i32) {
292
- CHECK_NIL(i32);
293
- write_varint32(GET_TRANSPORT(self), int_to_zig_zag(NUM2INT(i32)));
329
+ write_varint32(GET_TRANSPORT(self), int_to_zig_zag(checked_i32_value(i32)));
294
330
  return Qnil;
295
331
  }
296
332
 
297
333
  VALUE rb_thrift_compact_proto_write_i64(VALUE self, VALUE i64) {
298
- CHECK_NIL(i64);
299
- write_varint64(GET_TRANSPORT(self), ll_to_zig_zag(NUM2LL(i64)));
334
+ write_varint64(GET_TRANSPORT(self), ll_to_zig_zag(checked_i64_value(i64)));
300
335
  return Qnil;
301
336
  }
302
337
 
@@ -330,8 +365,12 @@ VALUE rb_thrift_compact_proto_write_string(VALUE self, VALUE str) {
330
365
  VALUE rb_thrift_compact_proto_write_binary(VALUE self, VALUE buf) {
331
366
  buf = force_binary_encoding(buf);
332
367
  VALUE transport = GET_TRANSPORT(self);
333
- write_varint32(transport, (uint32_t)RSTRING_LEN(buf));
334
- WRITE(transport, StringValuePtr(buf), RSTRING_LEN(buf));
368
+ long size = RSTRING_LEN(buf);
369
+ if (RB_UNLIKELY(size > INT32_MAX)) {
370
+ rb_raise(rb_eRangeError, "integer out of bounds");
371
+ }
372
+ write_varint32(transport, (uint32_t)size);
373
+ WRITE(transport, StringValuePtr(buf), size);
335
374
  return Qnil;
336
375
  }
337
376
 
@@ -416,10 +455,20 @@ static int8_t get_ttype(int8_t ctype) {
416
455
  } else if (ctype == CTYPE_UUID) {
417
456
  return TTYPE_UUID;
418
457
  } else {
419
- char str[50];
420
- sprintf(str, "don't know what type: %d", ctype);
421
- rb_raise(rb_eStandardError, "%s", str);
422
- return 0;
458
+ rb_exc_raise(get_protocol_exception(
459
+ INT2FIX(PROTOERR_INVALID_DATA),
460
+ rb_sprintf("Unknown compact type: %d", ctype)
461
+ ));
462
+ return 0; /* unreachable */
463
+ }
464
+ }
465
+
466
+ static inline void validate_container_size(uint32_t size) {
467
+ if (RB_UNLIKELY(size > INT32_MAX)) {
468
+ rb_exc_raise(get_protocol_exception(
469
+ INT2FIX(PROTOERR_SIZE_LIMIT),
470
+ rb_str_new2("Container size limit exceeded")
471
+ ));
423
472
  }
424
473
  }
425
474
 
@@ -437,6 +486,7 @@ static int32_t zig_zag_to_int(uint32_t n) {
437
486
  }
438
487
 
439
488
  #define MAX_VARINT32_BYTES 5 /* ceil(32/7); matches protobuf wire format */
489
+ #define MAX_VARINT32_LAST_BYTE 0x0f
440
490
  #define MAX_VARINT64_BYTES 10 /* ceil(64/7); matches protobuf wire format */
441
491
 
442
492
  static uint64_t read_varint64(VALUE self) {
@@ -457,7 +507,7 @@ static uint64_t read_varint64(VALUE self) {
457
507
  static uint32_t read_varint32(VALUE self) {
458
508
  int i, shift = 0;
459
509
  uint32_t result = 0;
460
- for (i = 0; i < MAX_VARINT32_BYTES; i++) {
510
+ for (i = 0; i < MAX_VARINT32_BYTES - 1; i++) {
461
511
  int8_t b = read_byte_direct(self);
462
512
  result |= ((uint32_t)(b & 0x7f) << shift);
463
513
  if ((b & 0x80) != 0x80) {
@@ -465,8 +515,15 @@ static uint32_t read_varint32(VALUE self) {
465
515
  }
466
516
  shift += 7;
467
517
  }
468
- rb_exc_raise(get_protocol_exception(INT2FIX(PROTOERR_INVALID_DATA), rb_str_new2("Variable-length int over 5 bytes.")));
469
- return 0; /* unreachable */
518
+
519
+ int8_t b = read_byte_direct(self);
520
+ if (RB_UNLIKELY((b & 0x80) != 0)) {
521
+ rb_exc_raise(get_protocol_exception(INT2FIX(PROTOERR_INVALID_DATA), rb_str_new2("Variable-length int over 5 bytes.")));
522
+ }
523
+ if (RB_UNLIKELY((b & ~MAX_VARINT32_LAST_BYTE) != 0)) {
524
+ rb_exc_raise(get_protocol_exception(INT2FIX(PROTOERR_INVALID_DATA), rb_str_new2("Variable-length int overflows uint32.")));
525
+ }
526
+ return result | ((uint32_t)b << shift);
470
527
  }
471
528
 
472
529
  static int16_t read_i16(VALUE self) {
@@ -504,21 +561,23 @@ VALUE rb_thrift_compact_proto_read_set_end(VALUE self) {
504
561
  }
505
562
 
506
563
  VALUE rb_thrift_compact_proto_read_message_begin(VALUE self) {
564
+ if (RTEST(rb_ivar_get(self, reset_message_size_ivar_id))) {
565
+ rb_funcall(GET_TRANSPORT(self), reset_message_size_method_id, 0);
566
+ }
567
+
507
568
  int8_t protocol_id = read_byte_direct(self);
508
569
  if (protocol_id != PROTOCOL_ID) {
509
570
  char buf[100];
510
- int len = sprintf(buf, "Expected protocol id %d but got %d", PROTOCOL_ID, protocol_id);
511
- buf[len] = 0;
512
- rb_exc_raise(get_protocol_exception(INT2FIX(-1), rb_str_new2(buf)));
571
+ snprintf(buf, sizeof(buf), "Expected protocol id %d but got %d", PROTOCOL_ID, protocol_id);
572
+ rb_exc_raise(get_protocol_exception(INT2FIX(PROTOERR_BAD_VERSION), rb_str_new2(buf)));
513
573
  }
514
574
 
515
575
  int8_t version_and_type = read_byte_direct(self);
516
576
  int8_t version = version_and_type & VERSION_MASK;
517
577
  if (version != VERSION) {
518
578
  char buf[100];
519
- int len = sprintf(buf, "Expected version id %d but got %d", version, VERSION);
520
- buf[len] = 0;
521
- rb_exc_raise(get_protocol_exception(INT2FIX(-1), rb_str_new2(buf)));
579
+ snprintf(buf, sizeof(buf), "Expected version %d but got %d", VERSION, version);
580
+ rb_exc_raise(get_protocol_exception(INT2FIX(PROTOERR_BAD_VERSION), rb_str_new2(buf)));
522
581
  }
523
582
 
524
583
  int8_t type = (version_and_type >> TYPE_SHIFT_AMOUNT) & TYPE_BITS;
@@ -561,6 +620,7 @@ VALUE rb_thrift_compact_proto_read_field_begin(VALUE self) {
561
620
 
562
621
  VALUE rb_thrift_compact_proto_read_map_begin(VALUE self) {
563
622
  uint32_t size = read_varint32(self);
623
+ validate_container_size(size);
564
624
  uint8_t key_and_value_type = size == 0 ? 0 : read_byte_direct(self);
565
625
  return rb_ary_new3(3, INT2FIX(get_ttype(key_and_value_type >> 4)), INT2FIX(get_ttype(key_and_value_type & 0xf)), UINT2NUM(size));
566
626
  }
@@ -571,6 +631,7 @@ VALUE rb_thrift_compact_proto_read_list_begin(VALUE self) {
571
631
  if (size == 15) {
572
632
  size = read_varint32(self);
573
633
  }
634
+ validate_container_size(size);
574
635
  uint8_t type = get_ttype(size_and_type & 0x0f);
575
636
  return rb_ary_new3(2, INT2FIX(type), UINT2NUM(size));
576
637
  }
@@ -608,19 +669,19 @@ VALUE rb_thrift_compact_proto_read_i64(VALUE self) {
608
669
  VALUE rb_thrift_compact_proto_read_double(VALUE self) {
609
670
  union {
610
671
  double f;
611
- int64_t l;
672
+ uint64_t l;
612
673
  } transfer;
613
674
  VALUE rbuf = rb_ivar_get(self, rbuf_ivar_id);
614
675
  rb_funcall(GET_TRANSPORT(self), read_into_buffer_method_id, 2, rbuf, INT2FIX(8));
615
- uint32_t lo = ((uint8_t)(RSTRING_PTR(rbuf)[0]))
616
- | (((uint8_t)(RSTRING_PTR(rbuf)[1])) << 8)
617
- | (((uint8_t)(RSTRING_PTR(rbuf)[2])) << 16)
618
- | (((uint8_t)(RSTRING_PTR(rbuf)[3])) << 24);
619
- uint64_t hi = (((uint8_t)(RSTRING_PTR(rbuf)[4])))
620
- | (((uint8_t)(RSTRING_PTR(rbuf)[5])) << 8)
621
- | (((uint8_t)(RSTRING_PTR(rbuf)[6])) << 16)
622
- | (((uint8_t)(RSTRING_PTR(rbuf)[7])) << 24);
623
- transfer.l = (hi << 32) | lo;
676
+ const uint8_t* bytes = (const uint8_t*)RSTRING_PTR(rbuf);
677
+ transfer.l = (uint64_t)bytes[0]
678
+ | ((uint64_t)bytes[1] << 8)
679
+ | ((uint64_t)bytes[2] << 16)
680
+ | ((uint64_t)bytes[3] << 24)
681
+ | ((uint64_t)bytes[4] << 32)
682
+ | ((uint64_t)bytes[5] << 40)
683
+ | ((uint64_t)bytes[6] << 48)
684
+ | ((uint64_t)bytes[7] << 56);
624
685
 
625
686
  return rb_float_new(transfer.f);
626
687
  }
@@ -632,6 +693,10 @@ VALUE rb_thrift_compact_proto_read_string(VALUE self) {
632
693
 
633
694
  VALUE rb_thrift_compact_proto_read_binary(VALUE self) {
634
695
  uint32_t size = read_varint32(self);
696
+ if (RB_UNLIKELY(size > INT32_MAX)) {
697
+ rb_exc_raise(get_protocol_exception(INT2FIX(PROTOERR_SIZE_LIMIT), rb_str_new2("Binary size limit exceeded")));
698
+ }
699
+ CHECK_STRING_SIZE(self, size);
635
700
  return rb_funcall(GET_TRANSPORT(self), read_all_method_id, 1, UINT2NUM(size));
636
701
  }
637
702