diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 2da3a59d3a5..1353f603e7e 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -641,6 +641,45 @@ jobs:
path: lib/cpp/test/*.xml
retention-days: 3
+ lib-ruby:
+ needs: compiler
+ runs-on: ubuntu-24.04
+ strategy:
+ matrix:
+ ruby-version: ["2.7", "3.0", "3.1", "3.2", "3.3", "3.4", "4.0.0-preview2"]
+ fail-fast: false
+ steps:
+ - uses: actions/checkout@v5
+
+ - name: Set up Ruby
+ uses: ruby/setup-ruby@v1
+ with:
+ ruby-version: ${{ matrix.ruby-version }}
+ bundler-cache: true
+ working-directory: lib/rb
+
+ - name: Run bootstrap
+ run: ./bootstrap.sh
+
+ - name: Run configure
+ run: |
+ ./configure $(echo $CONFIG_ARGS_FOR_LIBS | sed 's/without-ruby/with-ruby/')
+
+ - uses: actions/download-artifact@v6
+ with:
+ name: thrift-compiler
+ path: compiler/cpp
+
+ - name: Run thrift-compiler
+ run: |
+ chmod a+x compiler/cpp/thrift
+ compiler/cpp/thrift -version
+
+ - name: Run tests
+ run: |
+ cd lib/rb
+ bundle exec rake spec
+
cross-test:
needs:
- lib-java-kotlin
diff --git a/LANGUAGES.md b/LANGUAGES.md
index baed8cec302..1fd76ea1ed7 100644
--- a/LANGUAGES.md
+++ b/LANGUAGES.md
@@ -312,8 +312,8 @@ Thrift's core protocol is TBinary, supported by all languages except for JavaScr
Ruby |
0.2.0 |
 |  |
-2.3.1p112 | 2.5.1p57 |
- |
+2.7.8 | 4.0.0 |
+ |
 |  |  |  |  |  |
 |  |  |  |
 |  |  |  |
diff --git a/compiler/cpp/src/thrift/generate/t_rb_generator.cc b/compiler/cpp/src/thrift/generate/t_rb_generator.cc
index e9465736692..9b093143b88 100644
--- a/compiler/cpp/src/thrift/generate/t_rb_generator.cc
+++ b/compiler/cpp/src/thrift/generate/t_rb_generator.cc
@@ -430,6 +430,9 @@ t_rb_ofstream& t_rb_generator::render_const_value(t_rb_ofstream& out,
case t_base_type::TYPE_STRING:
out << "%q\"" << get_escaped_string(value) << '"';
break;
+ case t_base_type::TYPE_UUID:
+ out << "%q\"" << get_escaped_string(value) << '"';
+ break;
case t_base_type::TYPE_BOOL:
out << (value->get_integer() > 0 ? "true" : "false");
break;
@@ -1175,6 +1178,8 @@ string t_rb_generator::type_to_enum(t_type* type) {
return "::Thrift::Types::I64";
case t_base_type::TYPE_DOUBLE:
return "::Thrift::Types::DOUBLE";
+ case t_base_type::TYPE_UUID:
+ return "::Thrift::Types::UUID";
default:
throw "compiler error: unhandled type";
}
diff --git a/lib/rb/ext/binary_protocol_accelerated.c b/lib/rb/ext/binary_protocol_accelerated.c
index c9ae4d299bb..6c094bc395d 100644
--- a/lib/rb/ext/binary_protocol_accelerated.c
+++ b/lib/rb/ext/binary_protocol_accelerated.c
@@ -20,6 +20,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -86,6 +87,19 @@ static void write_string_direct(VALUE trans, VALUE str) {
rb_funcall(trans, write_method_id, 1, str);
}
+// Efficient hex character to integer conversion
+static inline int hex_char_to_int(char c) {
+ if (c >= '0' && c <= '9') return c - '0';
+ if (c >= 'a' && c <= 'f') return c - 'a' + 10;
+ if (c >= 'A' && c <= 'F') return c - 'A' + 10;
+ return -1; // invalid hex character
+}
+
+// Efficient integer to hex character conversion
+static inline char int_to_hex_char(int val) {
+ return val < 10 ? ('0' + val) : ('a' + val - 10);
+}
+
//--------------------------------
// interface writing methods
//--------------------------------
@@ -228,6 +242,55 @@ VALUE rb_thrift_binary_proto_write_binary(VALUE self, VALUE buf) {
return Qnil;
}
+VALUE rb_thrift_binary_proto_write_uuid(VALUE self, VALUE uuid) {
+ CHECK_NIL(uuid);
+
+ if (TYPE(uuid) != T_STRING) {
+ rb_raise(rb_eStandardError, "UUID must be a string");
+ }
+
+ VALUE trans = GET_TRANSPORT(self);
+ char bytes[16];
+ const char* str = RSTRING_PTR(uuid);
+ long len = RSTRING_LEN(uuid);
+
+ // Parse UUID string (format: "550e8400-e29b-41d4-a716-446655440000")
+ // Expected length: 36 characters (32 hex + 4 hyphens)
+ if (len != 36 || str[8] != '-' || str[13] != '-' || str[18] != '-' || str[23] != '-') {
+ VALUE exception_class = rb_const_get(thrift_module, rb_intern("ProtocolException"));
+ VALUE invalid_data = rb_const_get(exception_class, rb_intern("INVALID_DATA"));
+ VALUE args[2];
+ args[0] = invalid_data;
+ args[1] = rb_str_new2("Invalid UUID format");
+ rb_exc_raise(rb_class_new_instance(2, args, exception_class));
+ }
+
+ // Parse hex string to bytes using direct conversion, skipping hyphens
+ int byte_idx = 0;
+ for (int i = 0; i < len && byte_idx < 16; i++) {
+ if (str[i] == '-') continue;
+
+ // Convert two hex characters to one byte
+ int high = hex_char_to_int(str[i]);
+ int low = hex_char_to_int(str[i + 1]);
+
+ if (high < 0 || low < 0) {
+ VALUE exception_class = rb_const_get(thrift_module, rb_intern("ProtocolException"));
+ VALUE invalid_data = rb_const_get(exception_class, rb_intern("INVALID_DATA"));
+ VALUE args[2];
+ args[0] = invalid_data;
+ args[1] = rb_str_new2("Invalid hex character in UUID");
+ rb_exc_raise(rb_class_new_instance(2, args, exception_class));
+ }
+
+ bytes[byte_idx++] = (unsigned char)((high << 4) | low);
+ i++; // skip next char since we processed two
+ }
+
+ WRITE(trans, bytes, 16);
+ return Qnil;
+}
+
//---------------------------------------
// interface reading methods
//---------------------------------------
@@ -400,6 +463,28 @@ VALUE rb_thrift_binary_proto_read_binary(VALUE self) {
return READ(self, size);
}
+VALUE rb_thrift_binary_proto_read_uuid(VALUE self) {
+ unsigned char bytes[16];
+ VALUE data = READ(self, 16);
+ memcpy(bytes, RSTRING_PTR(data), 16);
+
+ // Format as UUID string: "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
+ char uuid_str[37];
+ char* p = uuid_str;
+
+ for (int i = 0; i < 16; i++) {
+ *p++ = int_to_hex_char((bytes[i] >> 4) & 0x0F);
+ *p++ = int_to_hex_char(bytes[i] & 0x0F);
+ if (i == 3 || i == 5 || i == 7 || i == 9) {
+ *p++ = '-';
+ }
+ }
+
+ *p = '\0';
+
+ return rb_str_new(uuid_str, 36);
+}
+
void Init_binary_protocol_accelerated() {
VALUE thrift_binary_protocol_class = rb_const_get(thrift_module, rb_intern("BinaryProtocol"));
@@ -425,6 +510,7 @@ void Init_binary_protocol_accelerated() {
rb_define_method(bpa_class, "write_double", rb_thrift_binary_proto_write_double, 1);
rb_define_method(bpa_class, "write_string", rb_thrift_binary_proto_write_string, 1);
rb_define_method(bpa_class, "write_binary", rb_thrift_binary_proto_write_binary, 1);
+ rb_define_method(bpa_class, "write_uuid", rb_thrift_binary_proto_write_uuid, 1);
// unused methods
rb_define_method(bpa_class, "write_message_end", rb_thrift_binary_proto_write_message_end, 0);
rb_define_method(bpa_class, "write_struct_begin", rb_thrift_binary_proto_write_struct_begin, 1);
@@ -447,6 +533,7 @@ void Init_binary_protocol_accelerated() {
rb_define_method(bpa_class, "read_double", rb_thrift_binary_proto_read_double, 0);
rb_define_method(bpa_class, "read_string", rb_thrift_binary_proto_read_string, 0);
rb_define_method(bpa_class, "read_binary", rb_thrift_binary_proto_read_binary, 0);
+ rb_define_method(bpa_class, "read_uuid", rb_thrift_binary_proto_read_uuid, 0);
// unused methods
rb_define_method(bpa_class, "read_message_end", rb_thrift_binary_proto_read_message_end, 0);
rb_define_method(bpa_class, "read_struct_begin", rb_thrift_binary_proto_read_struct_begin, 0);
diff --git a/lib/rb/ext/compact_protocol.c b/lib/rb/ext/compact_protocol.c
index c98c09eb491..7feaf748b40 100644
--- a/lib/rb/ext/compact_protocol.c
+++ b/lib/rb/ext/compact_protocol.c
@@ -58,6 +58,7 @@ static int CTYPE_LIST = 0x09;
static int CTYPE_SET = 0x0A;
static int CTYPE_MAP = 0x0B;
static int CTYPE_STRUCT = 0x0C;
+static int CTYPE_UUID = 0x0D;
VALUE rb_thrift_compact_proto_write_i16(VALUE self, VALUE i16);
@@ -86,6 +87,8 @@ static int get_compact_type(VALUE type_value) {
return CTYPE_MAP;
} else if (type == TTYPE_STRUCT) {
return CTYPE_STRUCT;
+ } else if (type == TTYPE_UUID) {
+ return CTYPE_UUID;
} else {
char str[50];
sprintf(str, "don't know what type: %d", type);
@@ -357,6 +360,8 @@ static int8_t get_ttype(int8_t ctype) {
return TTYPE_MAP;
} else if (ctype == CTYPE_STRUCT) {
return TTYPE_STRUCT;
+ } else if (ctype == CTYPE_UUID) {
+ return TTYPE_UUID;
} else {
char str[50];
sprintf(str, "don't know what type: %d", ctype);
diff --git a/lib/rb/ext/constants.h b/lib/rb/ext/constants.h
index e7aec44787a..4fda2c0a345 100644
--- a/lib/rb/ext/constants.h
+++ b/lib/rb/ext/constants.h
@@ -29,6 +29,7 @@ extern int TTYPE_MAP;
extern int TTYPE_SET;
extern int TTYPE_LIST;
extern int TTYPE_STRUCT;
+extern int TTYPE_UUID;
extern ID validate_method_id;
extern ID write_struct_begin_method_id;
@@ -49,6 +50,7 @@ extern ID write_list_begin_method_id;
extern ID write_list_end_method_id;
extern ID write_set_begin_method_id;
extern ID write_set_end_method_id;
+extern ID write_uuid_method_id;
extern ID read_bool_method_id;
extern ID read_byte_method_id;
extern ID read_i16_method_id;
@@ -63,6 +65,7 @@ extern ID read_list_begin_method_id;
extern ID read_list_end_method_id;
extern ID read_set_begin_method_id;
extern ID read_set_end_method_id;
+extern ID read_uuid_method_id;
extern ID read_struct_begin_method_id;
extern ID read_struct_end_method_id;
extern ID read_field_begin_method_id;
diff --git a/lib/rb/ext/struct.c b/lib/rb/ext/struct.c
index e8255a9cbbf..f385a9f2c5a 100644
--- a/lib/rb/ext/struct.c
+++ b/lib/rb/ext/struct.c
@@ -75,6 +75,11 @@ VALUE default_write_string(VALUE protocol, VALUE value) {
return Qnil;
}
+VALUE default_write_uuid(VALUE protocol, VALUE value) {
+ rb_funcall(protocol, write_uuid_method_id, 1, value);
+ return Qnil;
+}
+
VALUE default_write_binary(VALUE protocol, VALUE value) {
rb_funcall(protocol, write_binary_method_id, 1, value);
return Qnil;
@@ -195,6 +200,10 @@ VALUE default_read_string(VALUE protocol) {
return rb_funcall(protocol, read_string_method_id, 0);
}
+VALUE default_read_uuid(VALUE protocol) {
+ return rb_funcall(protocol, read_uuid_method_id, 0);
+}
+
VALUE default_read_binary(VALUE protocol) {
return rb_funcall(protocol, read_binary_method_id, 0);
}
@@ -342,6 +351,8 @@ static void write_anything(int ttype, VALUE value, VALUE protocol, VALUE field_i
} else {
default_write_binary(protocol, value);
}
+ } else if (ttype == TTYPE_UUID) {
+ default_write_uuid(protocol, value);
} else if (IS_CONTAINER(ttype)) {
write_container(ttype, field_info, value, protocol);
} else if (ttype == TTYPE_STRUCT) {
@@ -452,6 +463,8 @@ static VALUE read_anything(VALUE protocol, int ttype, VALUE field_info) {
}
} else if (ttype == TTYPE_DOUBLE) {
result = default_read_double(protocol);
+ } else if (ttype == TTYPE_UUID) {
+ result = default_read_uuid(protocol);
} else if (ttype == TTYPE_STRUCT) {
VALUE klass = rb_hash_aref(field_info, class_sym);
result = rb_class_new_instance(0, NULL, klass);
diff --git a/lib/rb/ext/thrift_native.c b/lib/rb/ext/thrift_native.c
index d53545473a9..16a5ea4dd47 100644
--- a/lib/rb/ext/thrift_native.c
+++ b/lib/rb/ext/thrift_native.c
@@ -43,6 +43,7 @@ int TTYPE_MAP;
int TTYPE_SET;
int TTYPE_LIST;
int TTYPE_STRUCT;
+int TTYPE_UUID;
// method ids
ID validate_method_id;
@@ -57,6 +58,7 @@ ID write_i32_method_id;
ID write_i64_method_id;
ID write_double_method_id;
ID write_string_method_id;
+ID write_uuid_method_id;
ID write_binary_method_id;
ID write_map_begin_method_id;
ID write_map_end_method_id;
@@ -70,6 +72,7 @@ ID read_i16_method_id;
ID read_i32_method_id;
ID read_i64_method_id;
ID read_string_method_id;
+ID read_uuid_method_id;
ID read_binary_method_id;
ID read_double_method_id;
ID read_map_begin_method_id;
@@ -138,6 +141,7 @@ void Init_thrift_native() {
TTYPE_SET = FIX2INT(rb_const_get(thrift_types_module, rb_intern("SET")));
TTYPE_LIST = FIX2INT(rb_const_get(thrift_types_module, rb_intern("LIST")));
TTYPE_STRUCT = FIX2INT(rb_const_get(thrift_types_module, rb_intern("STRUCT")));
+ TTYPE_UUID = FIX2INT(rb_const_get(thrift_types_module, rb_intern("UUID")));
// method ids
validate_method_id = rb_intern("validate");
@@ -152,6 +156,7 @@ void Init_thrift_native() {
write_i64_method_id = rb_intern("write_i64");
write_double_method_id = rb_intern("write_double");
write_string_method_id = rb_intern("write_string");
+ write_uuid_method_id = rb_intern("write_uuid");
write_binary_method_id = rb_intern("write_binary");
write_map_begin_method_id = rb_intern("write_map_begin");
write_map_end_method_id = rb_intern("write_map_end");
@@ -165,10 +170,11 @@ void Init_thrift_native() {
read_i32_method_id = rb_intern("read_i32");
read_i64_method_id = rb_intern("read_i64");
read_string_method_id = rb_intern("read_string");
+ read_uuid_method_id = rb_intern("read_uuid");
read_binary_method_id = rb_intern("read_binary");
read_double_method_id = rb_intern("read_double");
read_map_begin_method_id = rb_intern("read_map_begin");
- read_map_end_method_id = rb_intern("read_map_end");
+ read_map_end_method_id = rb_intern("read_map_end");
read_list_begin_method_id = rb_intern("read_list_begin");
read_list_end_method_id = rb_intern("read_list_end");
read_set_begin_method_id = rb_intern("read_set_begin");
@@ -192,7 +198,7 @@ void Init_thrift_native() {
fields_const_id = rb_intern("FIELDS");
transport_ivar_id = rb_intern("@trans");
strict_read_ivar_id = rb_intern("@strict_read");
- strict_write_ivar_id = rb_intern("@strict_write");
+ strict_write_ivar_id = rb_intern("@strict_write");
// cached symbols
type_sym = ID2SYM(rb_intern("type"));
diff --git a/lib/rb/lib/thrift.rb b/lib/rb/lib/thrift.rb
index 0f581229c15..5d8369501ea 100644
--- a/lib/rb/lib/thrift.rb
+++ b/lib/rb/lib/thrift.rb
@@ -23,7 +23,6 @@
$:.unshift File.dirname(__FILE__)
require 'thrift/bytes'
-require 'thrift/core_ext'
require 'thrift/exceptions'
require 'thrift/types'
require 'thrift/processor'
diff --git a/lib/rb/lib/thrift/core_ext.rb b/lib/rb/lib/thrift/core_ext.rb
deleted file mode 100644
index f763cd5344d..00000000000
--- a/lib/rb/lib/thrift/core_ext.rb
+++ /dev/null
@@ -1,23 +0,0 @@
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-
-Dir[File.dirname(__FILE__) + "/core_ext/*.rb"].each do |file|
- name = File.basename(file, '.rb')
- require "thrift/core_ext/#{name}"
-end
diff --git a/lib/rb/lib/thrift/core_ext/fixnum.rb b/lib/rb/lib/thrift/core_ext/fixnum.rb
deleted file mode 100644
index b4fc90dd697..00000000000
--- a/lib/rb/lib/thrift/core_ext/fixnum.rb
+++ /dev/null
@@ -1,29 +0,0 @@
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-
-# Versions of ruby pre 1.8.7 do not have an .ord method available in the Fixnum
-# class.
-#
-if RUBY_VERSION < "1.8.7"
- class Fixnum
- def ord
- self
- end
- end
-end
\ No newline at end of file
diff --git a/lib/rb/lib/thrift/protocol/base_protocol.rb b/lib/rb/lib/thrift/protocol/base_protocol.rb
index 4d83a21ddb7..dfce44a3bc0 100644
--- a/lib/rb/lib/thrift/protocol/base_protocol.rb
+++ b/lib/rb/lib/thrift/protocol/base_protocol.rb
@@ -137,6 +137,15 @@ def write_binary(buf)
raise NotImplementedError
end
+ # Writes a UUID as 16 bytes.
+ #
+ # uuid - The UUID string to write (e.g. "550e8400-e29b-41d4-a716-446655440000").
+ #
+ # Returns nothing.
+ def write_uuid(uuid)
+ raise NotImplementedError
+ end
+
def read_message_begin
raise NotImplementedError
end
@@ -212,6 +221,13 @@ def read_binary
raise NotImplementedError
end
+ # Reads a UUID as 16 bytes and returns it as a string.
+ #
+ # Returns a String (e.g. "550e8400-e29b-41d4-a716-446655440000").
+ def read_uuid
+ raise NotImplementedError
+ end
+
# Writes a field based on the field information, field ID and value.
#
# field_info - A Hash containing the definition of the field:
@@ -251,9 +267,9 @@ def write_field(*args)
#
# Returns nothing.
def write_type(field_info, value)
- # if field_info is a Fixnum, assume it is a Thrift::Types constant
+ # if field_info is a Integer, assume it is a Thrift::Types constant
# convert it into a field_info Hash for backwards compatibility
- if field_info.is_a? Fixnum
+ if field_info.is_a? Integer
field_info = {:type => field_info}
end
@@ -276,6 +292,8 @@ def write_type(field_info, value)
else
write_string(value)
end
+ when Types::UUID
+ write_uuid(value)
when Types::STRUCT
value.write(self)
else
@@ -291,9 +309,9 @@ def write_type(field_info, value)
#
# Returns the value read; object type varies based on field_info[:type].
def read_type(field_info)
- # if field_info is a Fixnum, assume it is a Thrift::Types constant
+ # if field_info is a Integer, assume it is a Thrift::Types constant
# convert it into a field_info Hash for backwards compatibility
- if field_info.is_a? Fixnum
+ if field_info.is_a? Integer
field_info = {:type => field_info}
end
@@ -316,6 +334,8 @@ def read_type(field_info)
else
read_string
end
+ when Types::UUID
+ read_uuid
else
raise NotImplementedError
end
@@ -337,6 +357,8 @@ def skip(type)
read_double
when Types::STRING
read_string
+ when Types::UUID
+ read_uuid
when Types::STRUCT
read_struct_begin
while true
diff --git a/lib/rb/lib/thrift/protocol/binary_protocol.rb b/lib/rb/lib/thrift/protocol/binary_protocol.rb
index d8279dbe6b5..780a9d3ed1f 100644
--- a/lib/rb/lib/thrift/protocol/binary_protocol.rb
+++ b/lib/rb/lib/thrift/protocol/binary_protocol.rb
@@ -116,6 +116,24 @@ def write_binary(buf)
trans.write(buf)
end
+ def write_uuid(uuid)
+ # Validate UUID format: "550e8400-e29b-41d4-a716-446655440000"
+ # Expected length: 36 characters (32 hex + 4 hyphens)
+ unless uuid.is_a?(String) && uuid.length == 36 &&
+ uuid[8] == '-' && uuid[13] == '-' && uuid[18] == '-' && uuid[23] == '-'
+ raise ProtocolException.new(ProtocolException::INVALID_DATA, 'Invalid UUID format')
+ end
+
+ # Pack hex directly without creating intermediate string
+ # Check for valid hex characters during packing
+ hex_str = uuid.delete('-')
+ unless hex_str =~ /\A[0-9a-fA-F]{32}\z/
+ raise ProtocolException.new(ProtocolException::INVALID_DATA, 'Invalid hex characters in UUID')
+ end
+
+ trans.write([hex_str].pack('H*'))
+ end
+
def read_message_begin
version = read_i32
if version < 0
@@ -226,7 +244,13 @@ def read_binary
size = read_i32
trans.read_all(size)
end
-
+
+ def read_uuid
+ bytes = trans.read_all(16)
+ hex = bytes.unpack('H*')[0]
+ "#{hex[0, 8]}-#{hex[8, 4]}-#{hex[12, 4]}-#{hex[16, 4]}-#{hex[20, 12]}"
+ end
+
def to_s
"binary(#{super.to_s})"
end
diff --git a/lib/rb/lib/thrift/protocol/compact_protocol.rb b/lib/rb/lib/thrift/protocol/compact_protocol.rb
index 1f9bd30608f..b93c6e7f1ea 100644
--- a/lib/rb/lib/thrift/protocol/compact_protocol.rb
+++ b/lib/rb/lib/thrift/protocol/compact_protocol.rb
@@ -45,7 +45,8 @@ class CompactTypes
SET = 0x0A
MAP = 0x0B
STRUCT = 0x0C
-
+ UUID = 0x0D
+
def self.is_bool_type?(b)
(b & 0x0f) == BOOLEAN_TRUE || (b & 0x0f) == BOOLEAN_FALSE
end
@@ -63,7 +64,8 @@ def self.is_bool_type?(b)
LIST => Types::LIST,
SET => Types::SET,
MAP => Types::MAP,
- STRUCT => Types::STRUCT
+ STRUCT => Types::STRUCT,
+ UUID => Types::UUID
}
TTYPE_TO_COMPACT = {
@@ -78,7 +80,8 @@ def self.is_bool_type?(b)
Types::LIST => LIST,
Types::SET => SET,
Types::MAP => MAP,
- Types::STRUCT => STRUCT
+ Types::STRUCT => STRUCT,
+ Types::UUID => UUID
}
def self.get_ttype(compact_type)
@@ -220,6 +223,22 @@ def write_binary(buf)
@trans.write(buf)
end
+ def write_uuid(uuid)
+ # Validate UUID format: "550e8400-e29b-41d4-a716-446655440000"
+ unless uuid.is_a?(String) && uuid.length == 36 &&
+ uuid[8] == '-' && uuid[13] == '-' && uuid[18] == '-' && uuid[23] == '-'
+ raise ProtocolException.new(ProtocolException::INVALID_DATA, 'Invalid UUID format')
+ end
+
+ # Pack hex directly without creating intermediate string
+ hex_str = uuid.delete('-')
+ unless hex_str =~ /\A[0-9a-fA-F]{32}\z/
+ raise ProtocolException.new(ProtocolException::INVALID_DATA, 'Invalid hex characters in UUID')
+ end
+
+ @trans.write([hex_str].pack('H*'))
+ end
+
def read_message_begin
protocol_id = read_byte()
if protocol_id != PROTOCOL_ID
@@ -345,17 +364,23 @@ def read_binary
size = read_varint32()
trans.read_all(size)
end
-
+
+ def read_uuid
+ bytes = trans.read_all(16)
+ hex = bytes.unpack('H*')[0]
+ "#{hex[0, 8]}-#{hex[8, 4]}-#{hex[12, 4]}-#{hex[16, 4]}-#{hex[20, 12]}"
+ end
+
def to_s
"compact(#{super.to_s})"
end
private
-
- #
- # Abstract method for writing the start of lists and sets. List and sets on
+
+ #
+ # Abstract method for writing the start of lists and sets. List and sets on
# the wire differ only by the type indicator.
- #
+ #
def write_collection_begin(elem_type, size)
if size <= 14
write_byte(size << 4 | CompactTypes.get_compact_type(elem_type))
diff --git a/lib/rb/lib/thrift/protocol/json_protocol.rb b/lib/rb/lib/thrift/protocol/json_protocol.rb
index 91e74e46bf8..703e88d9134 100644
--- a/lib/rb/lib/thrift/protocol/json_protocol.rb
+++ b/lib/rb/lib/thrift/protocol/json_protocol.rb
@@ -16,9 +16,7 @@
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
-#
-
-require 'base64'
+#
module Thrift
class LookaheadReader
@@ -181,6 +179,8 @@ def get_type_name_for_type_id(id)
"set"
when Types::LIST
"lst"
+ when Types::UUID
+ "uid"
else
raise NotImplementedError
end
@@ -209,6 +209,8 @@ def get_type_id_for_type_name(name)
result = Types::SET
elsif (name == "lst")
result = Types::LIST
+ elsif (name == "uid")
+ result = Types::UUID
else
result = Types::STOP
end
@@ -311,7 +313,7 @@ def write_json_string(str)
def write_json_base64(str)
@context.write(trans)
trans.write(@@kJSONStringDelimiter)
- trans.write(Base64.strict_encode64(str))
+ trans.write([str].pack('m0'))
trans.write(@@kJSONStringDelimiter)
end
@@ -477,6 +479,10 @@ def write_binary(str)
write_json_base64(str)
end
+ def write_uuid(uuid)
+ write_json_string(uuid)
+ end
+
##
# Reading functions
##
@@ -555,7 +561,7 @@ def read_json_base64
str += '='
end
end
- Base64.strict_decode64(str)
+ str.unpack1('m0')
end
# Reads a sequence of characters, stopping at the first one that is not
@@ -769,6 +775,10 @@ def read_binary
read_json_base64
end
+ def read_uuid
+ read_json_string
+ end
+
def to_s
"json(#{super.to_s})"
end
diff --git a/lib/rb/lib/thrift/protocol/protocol_decorator.rb b/lib/rb/lib/thrift/protocol/protocol_decorator.rb
index b1e3c155d44..a080917fbb4 100644
--- a/lib/rb/lib/thrift/protocol/protocol_decorator.rb
+++ b/lib/rb/lib/thrift/protocol/protocol_decorator.rb
@@ -111,6 +111,10 @@ def write_binary(buf)
@protocol.write_binary(buf)
end
+ def write_uuid(uuid)
+ @protocol.write_uuid(uuid)
+ end
+
def read_message_begin
@protocol.read_message_begin
end
@@ -190,5 +194,9 @@ def read_string
def read_binary
@protocol.read_binary
end
+
+ def read_uuid
+ @protocol.read_uuid
+ end
end
end
\ No newline at end of file
diff --git a/lib/rb/lib/thrift/server/nonblocking_server.rb b/lib/rb/lib/thrift/server/nonblocking_server.rb
index 740f3417e20..f6ab54b787f 100644
--- a/lib/rb/lib/thrift/server/nonblocking_server.rb
+++ b/lib/rb/lib/thrift/server/nonblocking_server.rb
@@ -46,6 +46,10 @@ def serve
break if @server_transport.closed?
begin
rd, = select([@server_transport], nil, nil, 0.1)
+ rescue ::TypeError => e
+ # When select() is called with a closed transport with handler set to nil in
+ # shutdown paths, Ruby raises a TypeError ("#to_io gives NilClass").
+ break
rescue Errno::EBADF => e
# In Ruby 1.9, calling @server_transport.close in shutdown paths causes the select() to raise an
# Errno::EBADF. If this happens, ignore it and retry the loop.
diff --git a/lib/rb/lib/thrift/server/thin_http_server.rb b/lib/rb/lib/thrift/server/thin_http_server.rb
index 4a81c6d1776..bbd11755c32 100644
--- a/lib/rb/lib/thrift/server/thin_http_server.rb
+++ b/lib/rb/lib/thrift/server/thin_http_server.rb
@@ -60,9 +60,9 @@ def self.for(path, processor, protocol_factory)
run lambda { |env|
request = Rack::Request.new(env)
if RackApplication.valid_thrift_request?(request)
- RackApplication.successful_request(request, processor, protocol_factory)
+ RackApplication.successful_request(request, processor, protocol_factory).finish
else
- RackApplication.failed_request
+ RackApplication.failed_request.finish
end
}
end
diff --git a/lib/rb/lib/thrift/transport/base_transport.rb b/lib/rb/lib/thrift/transport/base_transport.rb
index 97e59352af7..12dd6731264 100644
--- a/lib/rb/lib/thrift/transport/base_transport.rb
+++ b/lib/rb/lib/thrift/transport/base_transport.rb
@@ -62,7 +62,7 @@ def read(sz)
raise NotImplementedError
end
- # Returns an unsigned byte as a Fixnum in the range (0..255).
+ # Returns an unsigned byte as a Integer in the range (0..255).
def read_byte
buf = read_all(1)
return Bytes.get_string_byte(buf, 0)
diff --git a/lib/rb/lib/thrift/types.rb b/lib/rb/lib/thrift/types.rb
index cac52691a7f..0f6ab42f81e 100644
--- a/lib/rb/lib/thrift/types.rb
+++ b/lib/rb/lib/thrift/types.rb
@@ -34,6 +34,7 @@ module Types
MAP = 13
SET = 14
LIST = 15
+ UUID = 16
end
class << self
@@ -56,6 +57,8 @@ def self.check_type(value, field, name, skip_nil=true)
Float
when Types::STRING
String
+ when Types::UUID
+ String
when Types::STRUCT
[Struct, Union]
when Types::MAP
diff --git a/lib/rb/spec/ThriftSpec.thrift b/lib/rb/spec/ThriftSpec.thrift
index b42481b32d3..3c2f3e0a875 100644
--- a/lib/rb/spec/ThriftSpec.thrift
+++ b/lib/rb/spec/ThriftSpec.thrift
@@ -60,6 +60,7 @@ union TestUnion {
3: i32 other_i32_field;
4: SomeEnum enum_field;
5: binary binary_field;
+ 6: uuid uuid_field;
}
struct Foo {
@@ -71,6 +72,7 @@ struct Foo {
6: set shorts = [5, 17, 239],
7: optional string opt_string
8: bool my_bool
+ 9: optional uuid opt_uuid
}
struct Foo2 {
@@ -92,7 +94,8 @@ struct SimpleList {
8: list