thrift 0.22.0 → 0.23.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 (112) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +175 -17
  3. data/benchmark/benchmark.rb +22 -8
  4. data/benchmark/client.rb +49 -6
  5. data/benchmark/server.rb +45 -7
  6. data/benchmark/thin_server.rb +1 -0
  7. data/ext/binary_protocol_accelerated.c +76 -19
  8. data/ext/compact_protocol.c +80 -15
  9. data/ext/constants.h +12 -0
  10. data/ext/extconf.rb +10 -9
  11. data/ext/memory_buffer.c +7 -7
  12. data/ext/protocol.c +29 -0
  13. data/ext/protocol.h +35 -0
  14. data/ext/struct.c +36 -5
  15. data/ext/thrift_native.c +27 -3
  16. data/lib/thrift/bytes.rb +68 -101
  17. data/lib/thrift/client.rb +61 -9
  18. data/lib/thrift/exceptions.rb +5 -5
  19. data/lib/thrift/multiplexed_processor.rb +6 -6
  20. data/lib/thrift/processor.rb +6 -6
  21. data/lib/thrift/protocol/base_protocol.rb +37 -15
  22. data/lib/thrift/protocol/binary_protocol.rb +25 -9
  23. data/lib/thrift/protocol/binary_protocol_accelerated.rb +5 -5
  24. data/lib/thrift/protocol/compact_protocol.rb +61 -37
  25. data/lib/thrift/protocol/header_protocol.rb +320 -0
  26. data/lib/thrift/protocol/json_protocol.rb +26 -16
  27. data/lib/thrift/protocol/multiplexed_protocol.rb +5 -5
  28. data/lib/thrift/protocol/protocol_decorator.rb +12 -4
  29. data/lib/thrift/serializer/deserializer.rb +5 -5
  30. data/lib/thrift/serializer/serializer.rb +4 -5
  31. data/lib/thrift/server/base_server.rb +4 -4
  32. data/lib/thrift/server/mongrel_http_server.rb +6 -6
  33. data/lib/thrift/server/nonblocking_server.rb +8 -8
  34. data/lib/thrift/server/simple_server.rb +4 -4
  35. data/lib/thrift/server/thin_http_server.rb +3 -3
  36. data/lib/thrift/server/thread_pool_server.rb +6 -6
  37. data/lib/thrift/server/threaded_server.rb +4 -4
  38. data/lib/thrift/struct.rb +11 -11
  39. data/lib/thrift/struct_union.rb +19 -9
  40. data/lib/thrift/thrift_native.rb +1 -1
  41. data/lib/thrift/transport/base_server_transport.rb +5 -5
  42. data/lib/thrift/transport/base_transport.rb +12 -12
  43. data/lib/thrift/transport/buffered_transport.rb +6 -6
  44. data/lib/thrift/transport/framed_transport.rb +7 -7
  45. data/lib/thrift/transport/header_transport.rb +516 -0
  46. data/lib/thrift/transport/http_client_transport.rb +1 -1
  47. data/lib/thrift/transport/io_stream_transport.rb +3 -3
  48. data/lib/thrift/transport/memory_buffer_transport.rb +6 -6
  49. data/lib/thrift/transport/server_socket.rb +8 -5
  50. data/lib/thrift/transport/socket.rb +58 -31
  51. data/lib/thrift/transport/ssl_server_socket.rb +1 -1
  52. data/lib/thrift/transport/ssl_socket.rb +2 -2
  53. data/lib/thrift/transport/unix_server_socket.rb +4 -4
  54. data/lib/thrift/transport/unix_socket.rb +6 -6
  55. data/lib/thrift/types.rb +9 -6
  56. data/lib/thrift/union.rb +14 -8
  57. data/lib/thrift/uuid.rb +49 -0
  58. data/lib/thrift.rb +3 -1
  59. data/spec/ThriftSpec.thrift +5 -1
  60. data/spec/base_protocol_spec.rb +1 -2
  61. data/spec/base_transport_spec.rb +6 -7
  62. data/spec/binary_protocol_spec.rb +0 -2
  63. data/spec/binary_protocol_spec_shared.rb +129 -142
  64. data/spec/bytes_spec.rb +57 -118
  65. data/spec/client_spec.rb +85 -19
  66. data/spec/compact_protocol_spec.rb +54 -16
  67. data/spec/constants_demo_spec.rb +101 -0
  68. data/spec/exception_spec.rb +0 -1
  69. data/spec/header_protocol_spec.rb +475 -0
  70. data/spec/header_transport_spec.rb +386 -0
  71. data/spec/http_client_spec.rb +4 -6
  72. data/spec/json_protocol_spec.rb +47 -47
  73. data/spec/namespaced_spec.rb +0 -1
  74. data/spec/nonblocking_server_spec.rb +102 -4
  75. data/spec/processor_spec.rb +0 -1
  76. data/spec/serializer_spec.rb +0 -1
  77. data/spec/server_socket_spec.rb +1 -1
  78. data/spec/server_spec.rb +8 -9
  79. data/spec/socket_spec.rb +0 -1
  80. data/spec/socket_spec_shared.rb +72 -9
  81. data/spec/spec_helper.rb +1 -1
  82. data/spec/ssl_server_socket_spec.rb +12 -1
  83. data/spec/ssl_socket_spec.rb +10 -1
  84. data/spec/struct_nested_containers_spec.rb +1 -2
  85. data/spec/struct_spec.rb +113 -9
  86. data/spec/support/header_protocol_helper.rb +54 -0
  87. data/spec/thin_http_server_spec.rb +3 -18
  88. data/spec/types_spec.rb +25 -26
  89. data/spec/union_spec.rb +69 -11
  90. data/spec/unix_socket_spec.rb +1 -2
  91. data/spec/uuid_validation_spec.rb +238 -0
  92. data/test/fuzz/Makefile.am +173 -0
  93. data/test/fuzz/README.md +149 -0
  94. data/test/fuzz/fuzz_common.rb +95 -0
  95. data/{lib/thrift/core_ext.rb → test/fuzz/fuzz_parse_binary_protocol.rb} +3 -4
  96. data/{lib/thrift/core_ext/fixnum.rb → test/fuzz/fuzz_parse_binary_protocol_accelerated.rb} +6 -13
  97. data/test/fuzz/fuzz_parse_binary_protocol_accelerated_harness.rb +22 -0
  98. data/test/fuzz/fuzz_parse_binary_protocol_harness.rb +22 -0
  99. data/test/fuzz/fuzz_parse_compact_protocol.rb +22 -0
  100. data/test/fuzz/fuzz_parse_compact_protocol_harness.rb +22 -0
  101. data/test/fuzz/fuzz_parse_json_protocol.rb +22 -0
  102. data/test/fuzz/fuzz_parse_json_protocol_harness.rb +22 -0
  103. data/test/fuzz/fuzz_roundtrip_binary_protocol.rb +22 -0
  104. data/test/fuzz/fuzz_roundtrip_binary_protocol_accelerated.rb +22 -0
  105. data/test/fuzz/fuzz_roundtrip_binary_protocol_accelerated_harness.rb +22 -0
  106. data/test/fuzz/fuzz_roundtrip_binary_protocol_harness.rb +22 -0
  107. data/test/fuzz/fuzz_roundtrip_compact_protocol.rb +22 -0
  108. data/test/fuzz/fuzz_roundtrip_compact_protocol_harness.rb +22 -0
  109. data/test/fuzz/fuzz_roundtrip_json_protocol.rb +22 -0
  110. data/test/fuzz/fuzz_roundtrip_json_protocol_harness.rb +22 -0
  111. data/test/fuzz/fuzz_tracer.rb +28 -0
  112. metadata +106 -37
data/ext/struct.c CHANGED
@@ -20,6 +20,7 @@
20
20
  #include "struct.h"
21
21
  #include "constants.h"
22
22
  #include "macros.h"
23
+ #include "protocol.h"
23
24
  #include "strlcpy.h"
24
25
 
25
26
  VALUE thrift_union_class;
@@ -34,6 +35,19 @@ static ID sorted_field_ids_method_id;
34
35
  #define IS_CONTAINER(ttype) ((ttype) == TTYPE_MAP || (ttype) == TTYPE_LIST || (ttype) == TTYPE_SET)
35
36
  #define STRUCT_FIELDS(obj) rb_const_get(CLASS_OF(obj), fields_const_id)
36
37
 
38
+ static VALUE new_container_array(int size) {
39
+ if (size < 0) {
40
+ rb_exc_raise(
41
+ get_protocol_exception(
42
+ INT2FIX(PROTOERR_NEGATIVE_SIZE),
43
+ rb_str_new2("Negative container size")
44
+ )
45
+ );
46
+ }
47
+
48
+ return rb_ary_new2(size > 1024 ? 1024 : size);
49
+ }
50
+
37
51
  //-------------------------------------------
38
52
  // Writing section
39
53
  //-------------------------------------------
@@ -75,6 +89,11 @@ VALUE default_write_string(VALUE protocol, VALUE value) {
75
89
  return Qnil;
76
90
  }
77
91
 
92
+ VALUE default_write_uuid(VALUE protocol, VALUE value) {
93
+ rb_funcall(protocol, write_uuid_method_id, 1, value);
94
+ return Qnil;
95
+ }
96
+
78
97
  VALUE default_write_binary(VALUE protocol, VALUE value) {
79
98
  rb_funcall(protocol, write_binary_method_id, 1, value);
80
99
  return Qnil;
@@ -195,6 +214,10 @@ VALUE default_read_string(VALUE protocol) {
195
214
  return rb_funcall(protocol, read_string_method_id, 0);
196
215
  }
197
216
 
217
+ VALUE default_read_uuid(VALUE protocol) {
218
+ return rb_funcall(protocol, read_uuid_method_id, 0);
219
+ }
220
+
198
221
  VALUE default_read_binary(VALUE protocol) {
199
222
  return rb_funcall(protocol, read_binary_method_id, 0);
200
223
  }
@@ -342,6 +365,8 @@ static void write_anything(int ttype, VALUE value, VALUE protocol, VALUE field_i
342
365
  } else {
343
366
  default_write_binary(protocol, value);
344
367
  }
368
+ } else if (ttype == TTYPE_UUID) {
369
+ default_write_uuid(protocol, value);
345
370
  } else if (IS_CONTAINER(ttype)) {
346
371
  write_container(ttype, field_info, value, protocol);
347
372
  } else if (ttype == TTYPE_STRUCT) {
@@ -452,6 +477,8 @@ static VALUE read_anything(VALUE protocol, int ttype, VALUE field_info) {
452
477
  }
453
478
  } else if (ttype == TTYPE_DOUBLE) {
454
479
  result = default_read_double(protocol);
480
+ } else if (ttype == TTYPE_UUID) {
481
+ result = default_read_uuid(protocol);
455
482
  } else if (ttype == TTYPE_STRUCT) {
456
483
  VALUE klass = rb_hash_aref(field_info, class_sym);
457
484
  result = rb_class_new_instance(0, NULL, klass);
@@ -469,6 +496,10 @@ static VALUE read_anything(VALUE protocol, int ttype, VALUE field_info) {
469
496
  int value_ttype = FIX2INT(rb_ary_entry(map_header, 1));
470
497
  int num_entries = FIX2INT(rb_ary_entry(map_header, 2));
471
498
 
499
+ if (num_entries < 0) {
500
+ rb_exc_raise(get_protocol_exception(INT2FIX(PROTOERR_NEGATIVE_SIZE), rb_str_new2("Negative container size")));
501
+ }
502
+
472
503
  // Check the declared key and value types against the expected ones and skip the map contents
473
504
  // if the types don't match.
474
505
  VALUE key_info = rb_hash_aref(field_info, key_sym);
@@ -509,7 +540,7 @@ static VALUE read_anything(VALUE protocol, int ttype, VALUE field_info) {
509
540
  if (!NIL_P(element_info)) {
510
541
  int specified_element_type = FIX2INT(rb_hash_aref(element_info, type_sym));
511
542
  if (specified_element_type == element_ttype) {
512
- result = rb_ary_new2(num_elements);
543
+ result = new_container_array(num_elements);
513
544
 
514
545
  for (i = 0; i < num_elements; ++i) {
515
546
  rb_ary_push(result, read_anything(protocol, element_ttype, rb_hash_aref(field_info, element_sym)));
@@ -536,7 +567,7 @@ static VALUE read_anything(VALUE protocol, int ttype, VALUE field_info) {
536
567
  if (!NIL_P(element_info)) {
537
568
  int specified_element_type = FIX2INT(rb_hash_aref(element_info, type_sym));
538
569
  if (specified_element_type == element_ttype) {
539
- items = rb_ary_new2(num_elements);
570
+ items = new_container_array(num_elements);
540
571
 
541
572
  for (i = 0; i < num_elements; ++i) {
542
573
  rb_ary_push(items, read_anything(protocol, element_ttype, rb_hash_aref(field_info, element_sym)));
@@ -643,7 +674,7 @@ static VALUE rb_thrift_union_read(VALUE self, VALUE protocol) {
643
674
  field_type = FIX2INT(field_type_value);
644
675
 
645
676
  if (field_type != TTYPE_STOP) {
646
- rb_raise(rb_eRuntimeError, "too many fields in union!");
677
+ rb_exc_raise(get_protocol_exception(INT2FIX(PROTOERR_INVALID_DATA), rb_str_new2("too many fields in union!")));
647
678
  }
648
679
 
649
680
  // read struct end
@@ -671,7 +702,7 @@ static VALUE rb_thrift_union_write(VALUE self, VALUE protocol) {
671
702
  VALUE field_info = rb_hash_aref(struct_fields, field_id);
672
703
 
673
704
  if(NIL_P(field_info)) {
674
- rb_raise(rb_eRuntimeError, "set_field is not valid for this union!");
705
+ rb_exc_raise(get_protocol_exception(INT2FIX(PROTOERR_INVALID_DATA), rb_str_new2("set_field is not valid for this union!")));
675
706
  }
676
707
 
677
708
  VALUE ttype_value = rb_hash_aref(field_info, type_sym);
@@ -691,7 +722,7 @@ static VALUE rb_thrift_union_write(VALUE self, VALUE protocol) {
691
722
  return Qnil;
692
723
  }
693
724
 
694
- void Init_struct() {
725
+ void Init_struct(void) {
695
726
  VALUE struct_module = rb_const_get(thrift_module, rb_intern("Struct"));
696
727
 
697
728
  rb_define_method(struct_module, "write", rb_thrift_struct_write, 1);
data/ext/thrift_native.c CHANGED
@@ -43,6 +43,7 @@ int TTYPE_MAP;
43
43
  int TTYPE_SET;
44
44
  int TTYPE_LIST;
45
45
  int TTYPE_STRUCT;
46
+ int TTYPE_UUID;
46
47
 
47
48
  // method ids
48
49
  ID validate_method_id;
@@ -57,6 +58,7 @@ ID write_i32_method_id;
57
58
  ID write_i64_method_id;
58
59
  ID write_double_method_id;
59
60
  ID write_string_method_id;
61
+ ID write_uuid_method_id;
60
62
  ID write_binary_method_id;
61
63
  ID write_map_begin_method_id;
62
64
  ID write_map_end_method_id;
@@ -70,6 +72,7 @@ ID read_i16_method_id;
70
72
  ID read_i32_method_id;
71
73
  ID read_i64_method_id;
72
74
  ID read_string_method_id;
75
+ ID read_uuid_method_id;
73
76
  ID read_binary_method_id;
74
77
  ID read_double_method_id;
75
78
  ID read_map_begin_method_id;
@@ -109,7 +112,16 @@ VALUE class_sym;
109
112
  VALUE binary_sym;
110
113
  VALUE protocol_exception_class;
111
114
 
112
- void Init_thrift_native() {
115
+ // protocol errors
116
+ int PROTOERR_UNKNOWN;
117
+ int PROTOERR_INVALID_DATA;
118
+ int PROTOERR_NEGATIVE_SIZE;
119
+ int PROTOERR_SIZE_LIMIT;
120
+ int PROTOERR_BAD_VERSION;
121
+ int PROTOERR_NOT_IMPLEMENTED;
122
+ int PROTOERR_DEPTH_LIMIT;
123
+
124
+ RUBY_FUNC_EXPORTED void Init_thrift_native(void) {
113
125
  // cached classes
114
126
  thrift_module = rb_const_get(rb_cObject, rb_intern("Thrift"));
115
127
  rb_global_variable(&thrift_module);
@@ -138,6 +150,7 @@ void Init_thrift_native() {
138
150
  TTYPE_SET = FIX2INT(rb_const_get(thrift_types_module, rb_intern("SET")));
139
151
  TTYPE_LIST = FIX2INT(rb_const_get(thrift_types_module, rb_intern("LIST")));
140
152
  TTYPE_STRUCT = FIX2INT(rb_const_get(thrift_types_module, rb_intern("STRUCT")));
153
+ TTYPE_UUID = FIX2INT(rb_const_get(thrift_types_module, rb_intern("UUID")));
141
154
 
142
155
  // method ids
143
156
  validate_method_id = rb_intern("validate");
@@ -152,6 +165,7 @@ void Init_thrift_native() {
152
165
  write_i64_method_id = rb_intern("write_i64");
153
166
  write_double_method_id = rb_intern("write_double");
154
167
  write_string_method_id = rb_intern("write_string");
168
+ write_uuid_method_id = rb_intern("write_uuid");
155
169
  write_binary_method_id = rb_intern("write_binary");
156
170
  write_map_begin_method_id = rb_intern("write_map_begin");
157
171
  write_map_end_method_id = rb_intern("write_map_end");
@@ -165,10 +179,11 @@ void Init_thrift_native() {
165
179
  read_i32_method_id = rb_intern("read_i32");
166
180
  read_i64_method_id = rb_intern("read_i64");
167
181
  read_string_method_id = rb_intern("read_string");
182
+ read_uuid_method_id = rb_intern("read_uuid");
168
183
  read_binary_method_id = rb_intern("read_binary");
169
184
  read_double_method_id = rb_intern("read_double");
170
185
  read_map_begin_method_id = rb_intern("read_map_begin");
171
- read_map_end_method_id = rb_intern("read_map_end");
186
+ read_map_end_method_id = rb_intern("read_map_end");
172
187
  read_list_begin_method_id = rb_intern("read_list_begin");
173
188
  read_list_end_method_id = rb_intern("read_list_end");
174
189
  read_set_begin_method_id = rb_intern("read_set_begin");
@@ -192,7 +207,7 @@ void Init_thrift_native() {
192
207
  fields_const_id = rb_intern("FIELDS");
193
208
  transport_ivar_id = rb_intern("@trans");
194
209
  strict_read_ivar_id = rb_intern("@strict_read");
195
- strict_write_ivar_id = rb_intern("@strict_write");
210
+ strict_write_ivar_id = rb_intern("@strict_write");
196
211
 
197
212
  // cached symbols
198
213
  type_sym = ID2SYM(rb_intern("type"));
@@ -203,6 +218,15 @@ void Init_thrift_native() {
203
218
  class_sym = ID2SYM(rb_intern("class"));
204
219
  binary_sym = ID2SYM(rb_intern("binary"));
205
220
 
221
+ // protocol errors
222
+ PROTOERR_UNKNOWN = FIX2INT(rb_const_get(protocol_exception_class, rb_intern("UNKNOWN")));
223
+ PROTOERR_INVALID_DATA = FIX2INT(rb_const_get(protocol_exception_class, rb_intern("INVALID_DATA")));
224
+ PROTOERR_NEGATIVE_SIZE = FIX2INT(rb_const_get(protocol_exception_class, rb_intern("NEGATIVE_SIZE")));
225
+ PROTOERR_SIZE_LIMIT = FIX2INT(rb_const_get(protocol_exception_class, rb_intern("SIZE_LIMIT")));
226
+ PROTOERR_BAD_VERSION = FIX2INT(rb_const_get(protocol_exception_class, rb_intern("BAD_VERSION")));
227
+ PROTOERR_NOT_IMPLEMENTED = FIX2INT(rb_const_get(protocol_exception_class, rb_intern("NOT_IMPLEMENTED")));
228
+ PROTOERR_DEPTH_LIMIT = FIX2INT(rb_const_get(protocol_exception_class, rb_intern("DEPTH_LIMIT")));
229
+
206
230
  rb_global_variable(&type_sym);
207
231
  rb_global_variable(&name_sym);
208
232
  rb_global_variable(&key_sym);
data/lib/thrift/bytes.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # encoding: ascii-8bit
2
- #
2
+ #
3
3
  # Licensed to the Apache Software Foundation (ASF) under one
4
4
  # or more contributor license agreements. See the NOTICE file
5
5
  # distributed with this work for additional information
@@ -7,9 +7,9 @@
7
7
  # to you under the Apache License, Version 2.0 (the
8
8
  # "License"); you may not use this file except in compliance
9
9
  # with the License. You may obtain a copy of the License at
10
- #
10
+ #
11
11
  # http://www.apache.org/licenses/LICENSE-2.0
12
- #
12
+ #
13
13
  # Unless required by applicable law or agreed to in writing,
14
14
  # software distributed under the License is distributed on an
15
15
  # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@@ -21,111 +21,78 @@
21
21
  module Thrift
22
22
  # A collection of utilities for working with bytes and byte buffers.
23
23
  module Bytes
24
- if RUBY_VERSION >= '1.9'
25
- # Creates and empty byte buffer (String with BINARY encoding)
26
- #
27
- # size - The Integer size of the buffer (default: nil) to create
28
- #
29
- # Returns a String with BINARY encoding, filled with null characters
30
- # if size is greater than zero
31
- def self.empty_byte_buffer(size = nil)
32
- if (size && size > 0)
33
- "\0".force_encoding(Encoding::BINARY) * size
34
- else
35
- ''.force_encoding(Encoding::BINARY)
36
- end
37
- end
38
-
39
- # Forces the encoding of the buffer to BINARY. If the buffer
40
- # passed is frozen, then it will be duplicated.
41
- #
42
- # buffer - The String to force the encoding of.
43
- #
44
- # Returns the String passed with an encoding of BINARY; returned
45
- # String may be a duplicate.
46
- def self.force_binary_encoding(buffer)
47
- buffer = buffer.dup if buffer.frozen?
48
- buffer.force_encoding(Encoding::BINARY)
49
- end
50
-
51
- # Gets the byte value of a given position in a String.
52
- #
53
- # string - The String to retrive the byte value from.
54
- # index - The Integer location of the byte value to retrieve.
55
- #
56
- # Returns an Integer value between 0 and 255.
57
- def self.get_string_byte(string, index)
58
- string.getbyte(index)
59
- end
60
-
61
- # Sets the byte value given to a given index in a String.
62
- #
63
- # string - The String to set the byte value in.
64
- # index - The Integer location to set the byte value at.
65
- # byte - The Integer value (0 to 255) to set in the string.
66
- #
67
- # Returns an Integer value of the byte value to set.
68
- def self.set_string_byte(string, index, byte)
69
- string.setbyte(index, byte)
70
- end
71
-
72
- # Converts the given String to a UTF-8 byte buffer.
73
- #
74
- # string - The String to convert.
75
- #
76
- # Returns a new String with BINARY encoding, containing the UTF-8
77
- # bytes of the original string.
78
- def self.convert_to_utf8_byte_buffer(string)
79
- if string.encoding != Encoding::UTF_8
80
- # transcode to UTF-8
81
- string = string.encode(Encoding::UTF_8)
82
- else
83
- # encoding is already UTF-8, but a duplicate is needed
84
- string = string.dup
85
- end
86
- string.force_encoding(Encoding::BINARY)
87
- end
88
-
89
- # Converts the given UTF-8 byte buffer into a String
90
- #
91
- # utf8_buffer - A String, with BINARY encoding, containing UTF-8 bytes
92
- #
93
- # Returns a new String with UTF-8 encoding,
94
- def self.convert_to_string(utf8_buffer)
95
- # duplicate the buffer, force encoding to UTF-8
96
- utf8_buffer.dup.force_encoding(Encoding::UTF_8)
97
- end
98
- else
99
- def self.empty_byte_buffer(size = nil)
100
- if (size && size > 0)
101
- "\0" * size
102
- else
103
- ''
104
- end
24
+ # Creates and empty byte buffer (String with BINARY encoding)
25
+ #
26
+ # size - The Integer size of the buffer (default: nil) to create
27
+ #
28
+ # Returns a String with BINARY encoding, filled with null characters
29
+ # if size is greater than zero
30
+ def self.empty_byte_buffer(size = nil)
31
+ if (size && size > 0)
32
+ "\0".b * size
33
+ else
34
+ ''.b
105
35
  end
36
+ end
106
37
 
107
- def self.force_binary_encoding(buffer)
108
- buffer
109
- end
38
+ # Forces the encoding of the buffer to BINARY. If the buffer
39
+ # passed is frozen, then it will be duplicated.
40
+ #
41
+ # buffer - The String to force the encoding of.
42
+ #
43
+ # Returns the String passed with an encoding of BINARY; returned
44
+ # String may be a duplicate.
45
+ def self.force_binary_encoding(buffer)
46
+ buffer = buffer.dup if buffer.frozen?
47
+ buffer.force_encoding(Encoding::BINARY)
48
+ end
110
49
 
111
- def self.get_string_byte(string, index)
112
- string[index]
113
- end
50
+ # Gets the byte value of a given position in a String.
51
+ #
52
+ # string - The String to retrive the byte value from.
53
+ # index - The Integer location of the byte value to retrieve.
54
+ #
55
+ # Returns an Integer value between 0 and 255.
56
+ def self.get_string_byte(string, index)
57
+ string.getbyte(index)
58
+ end
114
59
 
115
- def self.set_string_byte(string, index, byte)
116
- string[index] = byte
117
- end
60
+ # Sets the byte value given to a given index in a String.
61
+ #
62
+ # string - The String to set the byte value in.
63
+ # index - The Integer location to set the byte value at.
64
+ # byte - The Integer value (0 to 255) to set in the string.
65
+ #
66
+ # Returns an Integer value of the byte value to set.
67
+ def self.set_string_byte(string, index, byte)
68
+ string.setbyte(index, byte)
69
+ end
118
70
 
119
- def self.convert_to_utf8_byte_buffer(string)
120
- # This assumes $KCODE is 'UTF8'/'U', which would mean the String is already a UTF-8 byte buffer
121
- # TODO consider handling other $KCODE values and transcoding with iconv
122
- string
71
+ # Converts the given String to a UTF-8 byte buffer.
72
+ #
73
+ # string - The String to convert.
74
+ #
75
+ # Returns a new String with BINARY encoding, containing the UTF-8
76
+ # bytes of the original string.
77
+ def self.convert_to_utf8_byte_buffer(string)
78
+ if string.encoding != Encoding::UTF_8
79
+ # transcode to UTF-8
80
+ string = string.encode(Encoding::UTF_8)
81
+ else
82
+ # encoding is already UTF-8, but a duplicate is needed
83
+ string = string.dup
123
84
  end
85
+ string.force_encoding(Encoding::BINARY)
86
+ end
124
87
 
125
- def self.convert_to_string(utf8_buffer)
126
- # See comment in 'convert_to_utf8_byte_buffer' for relevant assumptions.
127
- utf8_buffer
128
- end
88
+ # Converts the given UTF-8 byte buffer into a String
89
+ #
90
+ # utf8_buffer - A String, with BINARY encoding, containing UTF-8 bytes
91
+ #
92
+ # Returns a new String with UTF-8 encoding,
93
+ def self.convert_to_string(utf8_buffer)
94
+ # duplicate the buffer, force encoding to UTF-8
95
+ utf8_buffer.dup.force_encoding(Encoding::UTF_8)
129
96
  end
130
97
  end
131
98
  end
data/lib/thrift/client.rb CHANGED
@@ -19,19 +19,25 @@
19
19
 
20
20
  module Thrift
21
21
  module Client
22
- def initialize(iprot, oprot=nil)
22
+ MIN_SEQUENCE_ID = -(2**31)
23
+ MAX_SEQUENCE_ID = (2**31) - 1
24
+
25
+ def initialize(iprot, oprot = nil)
23
26
  @iprot = iprot
24
27
  @oprot = oprot || iprot
25
28
  @seqid = 0
29
+ @pending_seqids = []
26
30
  end
27
31
 
28
32
  def send_message(name, args_class, args = {})
29
- @oprot.write_message_begin(name, MessageTypes::CALL, @seqid)
33
+ seqid = next_seqid!
34
+ @oprot.write_message_begin(name, MessageTypes::CALL, seqid)
30
35
  send_message_args(args_class, args)
36
+ @pending_seqids << seqid
31
37
  end
32
38
 
33
39
  def send_oneway_message(name, args_class, args = {})
34
- @oprot.write_message_begin(name, MessageTypes::ONEWAY, @seqid)
40
+ @oprot.write_message_begin(name, MessageTypes::ONEWAY, next_seqid!)
35
41
  send_message_args(args_class, args)
36
42
  end
37
43
 
@@ -56,8 +62,37 @@ module Thrift
56
62
  end
57
63
 
58
64
  def reply_seqid(rseqid)
59
- result = (rseqid==@seqid)?true:false
60
- result
65
+ expected_seqid = dequeue_pending_seqid
66
+ !expected_seqid.nil? && rseqid == expected_seqid
67
+ end
68
+
69
+ def validate_message_begin(fname, mtype, rseqid, expected_name)
70
+ expected_seqid = dequeue_pending_seqid
71
+
72
+ if mtype == MessageTypes::EXCEPTION
73
+ raise_application_exception
74
+ end
75
+
76
+ if mtype != MessageTypes::REPLY
77
+ raise ApplicationException.new(
78
+ ApplicationException::INVALID_MESSAGE_TYPE,
79
+ "#{expected_name} failed: invalid message type"
80
+ )
81
+ end
82
+
83
+ if fname != expected_name
84
+ raise ApplicationException.new(
85
+ ApplicationException::WRONG_METHOD_NAME,
86
+ "#{expected_name} failed: wrong method name"
87
+ )
88
+ end
89
+
90
+ return if !expected_seqid.nil? && rseqid == expected_seqid
91
+
92
+ raise ApplicationException.new(
93
+ ApplicationException::BAD_SEQUENCE_ID,
94
+ "#{expected_name} failed: out of sequence response"
95
+ )
61
96
  end
62
97
 
63
98
  def receive_message(result_klass)
@@ -69,11 +104,28 @@ module Thrift
69
104
 
70
105
  def handle_exception(mtype)
71
106
  if mtype == MessageTypes::EXCEPTION
72
- x = ApplicationException.new
73
- x.read(@iprot)
74
- @iprot.read_message_end
75
- raise x
107
+ dequeue_pending_seqid
108
+ raise_application_exception
76
109
  end
77
110
  end
111
+
112
+ private
113
+
114
+ def next_seqid!
115
+ seqid = @seqid
116
+ @seqid = (seqid == MAX_SEQUENCE_ID) ? MIN_SEQUENCE_ID : seqid + 1
117
+ seqid
118
+ end
119
+
120
+ def dequeue_pending_seqid
121
+ @pending_seqids.shift
122
+ end
123
+
124
+ def raise_application_exception
125
+ x = ApplicationException.new
126
+ x.read(@iprot)
127
+ @iprot.read_message_end
128
+ raise x
129
+ end
78
130
  end
79
131
  end
@@ -1,4 +1,4 @@
1
- #
1
+ #
2
2
  # Licensed to the Apache Software Foundation (ASF) under one
3
3
  # or more contributor license agreements. See the NOTICE file
4
4
  # distributed with this work for additional information
@@ -6,16 +6,16 @@
6
6
  # to you under the Apache License, Version 2.0 (the
7
7
  # "License"); you may not use this file except in compliance
8
8
  # with the License. You may obtain a copy of the License at
9
- #
9
+ #
10
10
  # http://www.apache.org/licenses/LICENSE-2.0
11
- #
11
+ #
12
12
  # Unless required by applicable law or agreed to in writing,
13
13
  # software distributed under the License is distributed on an
14
14
  # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15
15
  # KIND, either express or implied. See the License for the
16
16
  # specific language governing permissions and limitations
17
17
  # under the License.
18
- #
18
+ #
19
19
 
20
20
  module Thrift
21
21
  class Exception < StandardError
@@ -43,7 +43,7 @@ module Thrift
43
43
 
44
44
  attr_reader :type
45
45
 
46
- def initialize(type=UNKNOWN, message=nil)
46
+ def initialize(type = UNKNOWN, message = nil)
47
47
  super(message)
48
48
  @type = type
49
49
  end
@@ -1,4 +1,4 @@
1
- #
1
+ #
2
2
  # Licensed to the Apache Software Foundation (ASF) under one
3
3
  # or more contributor license agreements. See the NOTICE file
4
4
  # distributed with this work for additional information
@@ -6,9 +6,9 @@
6
6
  # to you under the Apache License, Version 2.0 (the
7
7
  # "License"); you may not use this file except in compliance
8
8
  # with the License. You may obtain a copy of the License at
9
- #
9
+ #
10
10
  # http://www.apache.org/licenses/LICENSE-2.0
11
- #
11
+ #
12
12
  # Unless required by applicable law or agreed to in writing,
13
13
  # software distributed under the License is distributed on an
14
14
  # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@@ -24,11 +24,11 @@ module Thrift
24
24
  def initialize
25
25
  @actual_processors = {}
26
26
  end
27
-
27
+
28
28
  def register_processor(service_name, processor)
29
29
  @actual_processors[service_name] = processor
30
30
  end
31
-
31
+
32
32
  def process(iprot, oprot)
33
33
  name, type, seqid = iprot.read_message_begin
34
34
  check_type(type)
@@ -63,7 +63,7 @@ module Thrift
63
63
  class StoredMessageProtocol < BaseProtocol
64
64
 
65
65
  include ProtocolDecorator
66
-
66
+
67
67
  def initialize(protocol, message_begin)
68
68
  super(protocol)
69
69
  @message_begin = message_begin
@@ -1,4 +1,4 @@
1
- #
1
+ #
2
2
  # Licensed to the Apache Software Foundation (ASF) under one
3
3
  # or more contributor license agreements. See the NOTICE file
4
4
  # distributed with this work for additional information
@@ -6,22 +6,22 @@
6
6
  # to you under the Apache License, Version 2.0 (the
7
7
  # "License"); you may not use this file except in compliance
8
8
  # with the License. You may obtain a copy of the License at
9
- #
9
+ #
10
10
  # http://www.apache.org/licenses/LICENSE-2.0
11
- #
11
+ #
12
12
  # Unless required by applicable law or agreed to in writing,
13
13
  # software distributed under the License is distributed on an
14
14
  # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15
15
  # KIND, either express or implied. See the License for the
16
16
  # specific language governing permissions and limitations
17
17
  # under the License.
18
- #
18
+ #
19
19
 
20
20
  require 'logger'
21
21
 
22
22
  module Thrift
23
23
  module Processor
24
- def initialize(handler, logger=nil)
24
+ def initialize(handler, logger = nil)
25
25
  @handler = handler
26
26
  if logger.nil?
27
27
  @logger = Logger.new(STDERR)
@@ -32,7 +32,7 @@ module Thrift
32
32
  end
33
33
 
34
34
  def process(iprot, oprot)
35
- name, type, seqid = iprot.read_message_begin
35
+ name, type, seqid = iprot.read_message_begin
36
36
  if respond_to?("process_#{name}")
37
37
  begin
38
38
  send("process_#{name}", seqid, iprot, oprot)