fl-thrift 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (83) hide show
  1. data/CHANGELOG +4 -0
  2. data/Manifest +81 -0
  3. data/README +43 -0
  4. data/Rakefile +102 -0
  5. data/benchmark/Benchmark.thrift +24 -0
  6. data/benchmark/benchmark.rb +271 -0
  7. data/benchmark/client.rb +74 -0
  8. data/benchmark/server.rb +82 -0
  9. data/benchmark/thin_server.rb +44 -0
  10. data/ext/binary_protocol_accelerated.c +474 -0
  11. data/ext/binary_protocol_accelerated.h +20 -0
  12. data/ext/compact_protocol.c +665 -0
  13. data/ext/compact_protocol.h +20 -0
  14. data/ext/constants.h +95 -0
  15. data/ext/extconf.rb +26 -0
  16. data/ext/macros.h +41 -0
  17. data/ext/memory_buffer.c +76 -0
  18. data/ext/memory_buffer.h +20 -0
  19. data/ext/protocol.c +185 -0
  20. data/ext/protocol.h +20 -0
  21. data/ext/struct.c +606 -0
  22. data/ext/struct.h +67 -0
  23. data/ext/thrift_native.c +194 -0
  24. data/lib/thrift.rb +59 -0
  25. data/lib/thrift/client.rb +62 -0
  26. data/lib/thrift/core_ext.rb +23 -0
  27. data/lib/thrift/core_ext/fixnum.rb +29 -0
  28. data/lib/thrift/exceptions.rb +82 -0
  29. data/lib/thrift/processor.rb +57 -0
  30. data/lib/thrift/protocol/base_protocol.rb +290 -0
  31. data/lib/thrift/protocol/binary_protocol.rb +225 -0
  32. data/lib/thrift/protocol/binary_protocol_accelerated.rb +35 -0
  33. data/lib/thrift/protocol/compact_protocol.rb +422 -0
  34. data/lib/thrift/serializer/deserializer.rb +33 -0
  35. data/lib/thrift/serializer/serializer.rb +34 -0
  36. data/lib/thrift/server/base_server.rb +31 -0
  37. data/lib/thrift/server/mongrel_http_server.rb +58 -0
  38. data/lib/thrift/server/nonblocking_server.rb +296 -0
  39. data/lib/thrift/server/simple_server.rb +43 -0
  40. data/lib/thrift/server/thread_pool_server.rb +75 -0
  41. data/lib/thrift/server/threaded_server.rb +47 -0
  42. data/lib/thrift/struct.rb +298 -0
  43. data/lib/thrift/thrift_native.rb +24 -0
  44. data/lib/thrift/transport/base_server_transport.rb +37 -0
  45. data/lib/thrift/transport/base_transport.rb +70 -0
  46. data/lib/thrift/transport/buffered_transport.rb +77 -0
  47. data/lib/thrift/transport/framed_transport.rb +90 -0
  48. data/lib/thrift/transport/http_client_transport.rb +45 -0
  49. data/lib/thrift/transport/io_stream_transport.rb +39 -0
  50. data/lib/thrift/transport/memory_buffer_transport.rb +96 -0
  51. data/lib/thrift/transport/server_socket.rb +63 -0
  52. data/lib/thrift/transport/socket.rb +136 -0
  53. data/lib/thrift/transport/unix_server_socket.rb +60 -0
  54. data/lib/thrift/transport/unix_socket.rb +40 -0
  55. data/lib/thrift/types.rb +101 -0
  56. data/script/proto_benchmark.rb +121 -0
  57. data/script/read_struct.rb +43 -0
  58. data/script/write_struct.rb +30 -0
  59. data/setup.rb +1585 -0
  60. data/spec/ThriftSpec.thrift +84 -0
  61. data/spec/base_protocol_spec.rb +160 -0
  62. data/spec/base_transport_spec.rb +351 -0
  63. data/spec/binary_protocol_accelerated_spec.rb +41 -0
  64. data/spec/binary_protocol_spec.rb +63 -0
  65. data/spec/binary_protocol_spec_shared.rb +375 -0
  66. data/spec/client_spec.rb +100 -0
  67. data/spec/compact_protocol_spec.rb +117 -0
  68. data/spec/exception_spec.rb +142 -0
  69. data/spec/http_client_spec.rb +49 -0
  70. data/spec/mongrel_http_server_spec.rb +117 -0
  71. data/spec/nonblocking_server_spec.rb +265 -0
  72. data/spec/processor_spec.rb +83 -0
  73. data/spec/serializer_spec.rb +69 -0
  74. data/spec/server_socket_spec.rb +80 -0
  75. data/spec/server_spec.rb +160 -0
  76. data/spec/socket_spec.rb +61 -0
  77. data/spec/socket_spec_shared.rb +104 -0
  78. data/spec/spec_helper.rb +60 -0
  79. data/spec/struct_spec.rb +252 -0
  80. data/spec/types_spec.rb +116 -0
  81. data/spec/unix_socket_spec.rb +108 -0
  82. data/thrift.gemspec +32 -0
  83. metadata +202 -0
@@ -0,0 +1,63 @@
1
+ # encoding: ascii-8bit
2
+ #
3
+ # Licensed to the Apache Software Foundation (ASF) under one
4
+ # or more contributor license agreements. See the NOTICE file
5
+ # distributed with this work for additional information
6
+ # regarding copyright ownership. The ASF licenses this file
7
+ # to you under the Apache License, Version 2.0 (the
8
+ # "License"); you may not use this file except in compliance
9
+ # with the License. You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing,
14
+ # software distributed under the License is distributed on an
15
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16
+ # KIND, either express or implied. See the License for the
17
+ # specific language governing permissions and limitations
18
+ # under the License.
19
+ #
20
+
21
+ require 'socket'
22
+
23
+ module Thrift
24
+ class ServerSocket < BaseServerTransport
25
+ # call-seq: initialize(host = nil, port)
26
+ def initialize(host_or_port, port = nil)
27
+ if port
28
+ @host = host_or_port
29
+ @port = port
30
+ else
31
+ @host = nil
32
+ @port = host_or_port
33
+ end
34
+ @handle = nil
35
+ end
36
+
37
+ attr_reader :handle
38
+
39
+ def listen
40
+ @handle = TCPServer.new(@host, @port)
41
+ end
42
+
43
+ def accept
44
+ unless @handle.nil?
45
+ sock = @handle.accept
46
+ trans = Socket.new
47
+ trans.handle = sock
48
+ trans
49
+ end
50
+ end
51
+
52
+ def close
53
+ @handle.close unless @handle.nil? or @handle.closed?
54
+ @handle = nil
55
+ end
56
+
57
+ def closed?
58
+ @handle.nil? or @handle.closed?
59
+ end
60
+
61
+ alias to_io handle
62
+ end
63
+ end
@@ -0,0 +1,136 @@
1
+ # encoding: ascii-8bit
2
+ #
3
+ # Licensed to the Apache Software Foundation (ASF) under one
4
+ # or more contributor license agreements. See the NOTICE file
5
+ # distributed with this work for additional information
6
+ # regarding copyright ownership. The ASF licenses this file
7
+ # to you under the Apache License, Version 2.0 (the
8
+ # "License"); you may not use this file except in compliance
9
+ # with the License. You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing,
14
+ # software distributed under the License is distributed on an
15
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16
+ # KIND, either express or implied. See the License for the
17
+ # specific language governing permissions and limitations
18
+ # under the License.
19
+ #
20
+
21
+ require 'socket'
22
+
23
+ module Thrift
24
+ class Socket < BaseTransport
25
+ def initialize(host='localhost', port=9090, timeout=nil)
26
+ @host = host
27
+ @port = port
28
+ @timeout = timeout
29
+ @desc = "#{host}:#{port}"
30
+ @handle = nil
31
+ end
32
+
33
+ attr_accessor :handle, :timeout
34
+
35
+ def open
36
+ begin
37
+ addrinfo = ::Socket::getaddrinfo(@host, @port).first
38
+ @handle = ::Socket.new(addrinfo[4], ::Socket::SOCK_STREAM, 0)
39
+ sockaddr = ::Socket.sockaddr_in(addrinfo[1], addrinfo[3])
40
+ begin
41
+ @handle.connect_nonblock(sockaddr)
42
+ rescue Errno::EINPROGRESS
43
+ unless IO.select(nil, [ @handle ], nil, @timeout)
44
+ raise TransportException.new(TransportException::NOT_OPEN, "Connection timeout to #{@desc}")
45
+ end
46
+ begin
47
+ @handle.connect_nonblock(sockaddr)
48
+ rescue Errno::EISCONN
49
+ end
50
+ end
51
+ @handle
52
+ rescue StandardError => e
53
+ raise TransportException.new(TransportException::NOT_OPEN, "Could not connect to #{@desc}: #{e}")
54
+ end
55
+ end
56
+
57
+ def open?
58
+ !@handle.nil? and !@handle.closed?
59
+ end
60
+
61
+ def write(str)
62
+ raise IOError, "closed stream" unless open?
63
+ begin
64
+ if @timeout.nil? or @timeout == 0
65
+ @handle.write(str)
66
+ else
67
+ len = 0
68
+ start = Time.now
69
+ while Time.now - start < @timeout
70
+ rd, wr, = IO.select(nil, [@handle], nil, @timeout)
71
+ if wr and not wr.empty?
72
+ len += @handle.write_nonblock(str[len..-1])
73
+ break if len >= str.length
74
+ end
75
+ end
76
+ if len < str.length
77
+ raise TransportException.new(TransportException::TIMED_OUT, "Socket: Timed out writing #{str.length} bytes to #{@desc}")
78
+ else
79
+ len
80
+ end
81
+ end
82
+ rescue TransportException => e
83
+ # pass this on
84
+ raise e
85
+ rescue StandardError => e
86
+ @handle.close
87
+ @handle = nil
88
+ raise TransportException.new(TransportException::NOT_OPEN, e.message)
89
+ end
90
+ end
91
+
92
+ def read(sz)
93
+ raise IOError, "closed stream" unless open?
94
+
95
+ begin
96
+ if @timeout.nil? or @timeout == 0
97
+ data = @handle.readpartial(sz)
98
+ else
99
+ # it's possible to interrupt select for something other than the timeout
100
+ # so we need to ensure we've waited long enough
101
+ start = Time.now
102
+ rd = nil # scoping
103
+ loop do
104
+ rd, = IO.select([@handle], nil, nil, @timeout)
105
+ break if (rd and not rd.empty?) or Time.now - start >= @timeout
106
+ end
107
+ if rd.nil? or rd.empty?
108
+ raise TransportException.new(TransportException::TIMED_OUT, "Socket: Timed out reading #{sz} bytes from #{@desc}")
109
+ else
110
+ data = @handle.readpartial(sz)
111
+ end
112
+ end
113
+ rescue TransportException => e
114
+ # don't let this get caught by the StandardError handler
115
+ raise e
116
+ rescue StandardError => e
117
+ @handle.close unless @handle.closed?
118
+ @handle = nil
119
+ raise TransportException.new(TransportException::NOT_OPEN, e.message)
120
+ end
121
+ if (data.nil? or data.length == 0)
122
+ raise TransportException.new(TransportException::UNKNOWN, "Socket: Could not read #{sz} bytes from #{@desc}")
123
+ end
124
+ data
125
+ end
126
+
127
+ def close
128
+ @handle.close unless @handle.nil? or @handle.closed?
129
+ @handle = nil
130
+ end
131
+
132
+ def to_io
133
+ @handle
134
+ end
135
+ end
136
+ end
@@ -0,0 +1,60 @@
1
+ # encoding: ascii-8bit
2
+ #
3
+ # Licensed to the Apache Software Foundation (ASF) under one
4
+ # or more contributor license agreements. See the NOTICE file
5
+ # distributed with this work for additional information
6
+ # regarding copyright ownership. The ASF licenses this file
7
+ # to you under the Apache License, Version 2.0 (the
8
+ # "License"); you may not use this file except in compliance
9
+ # with the License. You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing,
14
+ # software distributed under the License is distributed on an
15
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16
+ # KIND, either express or implied. See the License for the
17
+ # specific language governing permissions and limitations
18
+ # under the License.
19
+ #
20
+
21
+ require 'socket'
22
+
23
+ module Thrift
24
+ class UNIXServerSocket < BaseServerTransport
25
+ def initialize(path)
26
+ @path = path
27
+ @handle = nil
28
+ end
29
+
30
+ attr_accessor :handle
31
+
32
+ def listen
33
+ @handle = ::UNIXServer.new(@path)
34
+ end
35
+
36
+ def accept
37
+ unless @handle.nil?
38
+ sock = @handle.accept
39
+ trans = UNIXSocket.new(nil)
40
+ trans.handle = sock
41
+ trans
42
+ end
43
+ end
44
+
45
+ def close
46
+ if @handle
47
+ @handle.close unless @handle.closed?
48
+ @handle = nil
49
+ # UNIXServer doesn't delete the socket file, so we have to do it ourselves
50
+ File.delete(@path)
51
+ end
52
+ end
53
+
54
+ def closed?
55
+ @handle.nil? or @handle.closed?
56
+ end
57
+
58
+ alias to_io handle
59
+ end
60
+ end
@@ -0,0 +1,40 @@
1
+ # encoding: ascii-8bit
2
+ #
3
+ # Licensed to the Apache Software Foundation (ASF) under one
4
+ # or more contributor license agreements. See the NOTICE file
5
+ # distributed with this work for additional information
6
+ # regarding copyright ownership. The ASF licenses this file
7
+ # to you under the Apache License, Version 2.0 (the
8
+ # "License"); you may not use this file except in compliance
9
+ # with the License. You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing,
14
+ # software distributed under the License is distributed on an
15
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16
+ # KIND, either express or implied. See the License for the
17
+ # specific language governing permissions and limitations
18
+ # under the License.
19
+ #
20
+
21
+ require 'socket'
22
+
23
+ module Thrift
24
+ class UNIXSocket < Socket
25
+ def initialize(path, timeout=nil)
26
+ @path = path
27
+ @timeout = timeout
28
+ @desc = @path # for read()'s error
29
+ @handle = nil
30
+ end
31
+
32
+ def open
33
+ begin
34
+ @handle = ::UNIXSocket.new(@path)
35
+ rescue StandardError
36
+ raise TransportException.new(TransportException::NOT_OPEN, "Could not open UNIX socket at #{@path}")
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,101 @@
1
+ #
2
+ # Licensed to the Apache Software Foundation (ASF) under one
3
+ # or more contributor license agreements. See the NOTICE file
4
+ # distributed with this work for additional information
5
+ # regarding copyright ownership. The ASF licenses this file
6
+ # to you under the Apache License, Version 2.0 (the
7
+ # "License"); you may not use this file except in compliance
8
+ # with the License. You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing,
13
+ # software distributed under the License is distributed on an
14
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15
+ # KIND, either express or implied. See the License for the
16
+ # specific language governing permissions and limitations
17
+ # under the License.
18
+ #
19
+
20
+ require 'set'
21
+
22
+ module Thrift
23
+ module Types
24
+ STOP = 0
25
+ VOID = 1
26
+ BOOL = 2
27
+ BYTE = 3
28
+ DOUBLE = 4
29
+ I16 = 6
30
+ I32 = 8
31
+ I64 = 10
32
+ STRING = 11
33
+ STRUCT = 12
34
+ MAP = 13
35
+ SET = 14
36
+ LIST = 15
37
+ end
38
+
39
+ class << self
40
+ attr_accessor :type_checking
41
+ end
42
+
43
+ class TypeError < Exception
44
+ end
45
+
46
+ def self.check_type(value, field, name, skip_nil=true)
47
+ return if value.nil? and skip_nil
48
+ klasses = case field[:type]
49
+ when Types::VOID
50
+ NilClass
51
+ when Types::BOOL
52
+ [TrueClass, FalseClass]
53
+ when Types::BYTE, Types::I16, Types::I32, Types::I64
54
+ Integer
55
+ when Types::DOUBLE
56
+ Float
57
+ when Types::STRING
58
+ String
59
+ when Types::STRUCT
60
+ Struct
61
+ when Types::MAP
62
+ Hash
63
+ when Types::SET
64
+ Set
65
+ when Types::LIST
66
+ Array
67
+ end
68
+ valid = klasses && [*klasses].any? { |klass| klass === value }
69
+ raise TypeError, "Expected #{type_name(field[:type])}, received #{value.class} for field #{name}" unless valid
70
+ # check elements now
71
+ case field[:type]
72
+ when Types::MAP
73
+ value.each_pair do |k,v|
74
+ check_type(k, field[:key], "#{name}.key", false)
75
+ check_type(v, field[:value], "#{name}.value", false)
76
+ end
77
+ when Types::SET, Types::LIST
78
+ value.each do |el|
79
+ check_type(el, field[:element], "#{name}.element", false)
80
+ end
81
+ when Types::STRUCT
82
+ raise TypeError, "Expected #{field[:class]}, received #{value.class} for field #{name}" unless field[:class] == value.class
83
+ end
84
+ end
85
+
86
+ def self.type_name(type)
87
+ Types.constants.each do |const|
88
+ return "Types::#{const}" if Types.const_get(const) == type
89
+ end
90
+ nil
91
+ end
92
+
93
+ module MessageTypes
94
+ CALL = 1
95
+ REPLY = 2
96
+ EXCEPTION = 3
97
+ ONEWAY = 4
98
+ end
99
+ end
100
+
101
+ Thrift.type_checking = false if Thrift.type_checking.nil?
@@ -0,0 +1,121 @@
1
+ #
2
+ # Licensed to the Apache Software Foundation (ASF) under one
3
+ # or more contributor license agreements. See the NOTICE file
4
+ # distributed with this work for additional information
5
+ # regarding copyright ownership. The ASF licenses this file
6
+ # to you under the Apache License, Version 2.0 (the
7
+ # "License"); you may not use this file except in compliance
8
+ # with the License. You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing,
13
+ # software distributed under the License is distributed on an
14
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15
+ # KIND, either express or implied. See the License for the
16
+ # specific language governing permissions and limitations
17
+ # under the License.
18
+ #
19
+
20
+ require File.dirname(__FILE__) + "/../spec/spec_helper.rb"
21
+
22
+ require "benchmark"
23
+ # require "ruby-prof"
24
+
25
+ obj = Fixtures::COMPACT_PROTOCOL_TEST_STRUCT
26
+
27
+ HOW_MANY = 1_000
28
+
29
+ binser = Thrift::Serializer.new
30
+ bin_data = binser.serialize(obj)
31
+ bindeser = Thrift::Deserializer.new
32
+ accel_bin_ser = Thrift::Serializer.new(Thrift::BinaryProtocolAcceleratedFactory.new)
33
+ accel_bin_deser = Thrift::Deserializer.new(Thrift::BinaryProtocolAcceleratedFactory.new)
34
+
35
+ compact_ser = Thrift::Serializer.new(Thrift::CompactProtocolFactory.new)
36
+ compact_data = compact_ser.serialize(obj)
37
+ compact_deser = Thrift::Deserializer.new(Thrift::CompactProtocolFactory.new)
38
+
39
+ Benchmark.bm(60) do |reporter|
40
+ reporter.report("binary protocol, write") do
41
+ HOW_MANY.times do
42
+ binser.serialize(obj)
43
+ end
44
+ end
45
+
46
+ reporter.report("accelerated binary protocol, write") do
47
+ HOW_MANY.times do
48
+ accel_bin_ser.serialize(obj)
49
+ end
50
+ end
51
+
52
+ reporter.report("compact protocol, write") do
53
+ # RubyProf.start
54
+ HOW_MANY.times do
55
+ compact_ser.serialize(obj)
56
+ end
57
+ # result = RubyProf.stop
58
+ # printer = RubyProf::GraphHtmlPrinter.new(result)
59
+ # file = File.open("profile.html", "w+")
60
+ # printer.print(file, 0)
61
+ # file.close
62
+ end
63
+
64
+ reporter.report("binary protocol, read") do
65
+ HOW_MANY.times do
66
+ bindeser.deserialize(obj, bin_data)
67
+ end
68
+ end
69
+
70
+ reporter.report("accelerated binary protocol, read") do
71
+ HOW_MANY.times do
72
+ accel_bin_deser.deserialize(obj, bin_data)
73
+ end
74
+ end
75
+
76
+ reporter.report("compact protocol, read") do
77
+ HOW_MANY.times do
78
+ compact_deser.deserialize(obj, compact_data)
79
+ end
80
+ end
81
+
82
+
83
+ # f = File.new("/tmp/testfile", "w")
84
+ # proto = Thrift::BinaryProtocolAccelerated.new(Thrift::IOStreamTransport.new(Thrift::MemoryBufferTransport.new, f))
85
+ # reporter.report("accelerated binary protocol, write (to disk)") do
86
+ # HOW_MANY.times do
87
+ # obj.write(proto)
88
+ # end
89
+ # f.flush
90
+ # end
91
+ # f.close
92
+ #
93
+ # f = File.new("/tmp/testfile", "r")
94
+ # proto = Thrift::BinaryProtocolAccelerated.new(Thrift::IOStreamTransport.new(f, Thrift::MemoryBufferTransport.new))
95
+ # reporter.report("accelerated binary protocol, read (from disk)") do
96
+ # HOW_MANY.times do
97
+ # obj.read(proto)
98
+ # end
99
+ # end
100
+ # f.close
101
+ #
102
+ # f = File.new("/tmp/testfile", "w")
103
+ # reporter.report("compact protocol, write (to disk)") do
104
+ # proto = Thrift::CompactProtocol.new(Thrift::IOStreamTransport.new(Thrift::MemoryBufferTransport.new, f))
105
+ # HOW_MANY.times do
106
+ # obj.write(proto)
107
+ # end
108
+ # f.flush
109
+ # end
110
+ # f.close
111
+ #
112
+ # f = File.new("/tmp/testfile", "r")
113
+ # reporter.report("compact protocol, read (from disk)") do
114
+ # proto = Thrift::CompactProtocol.new(Thrift::IOStreamTransport.new(f, Thrift::MemoryBufferTransport.new))
115
+ # HOW_MANY.times do
116
+ # obj.read(proto)
117
+ # end
118
+ # end
119
+ # f.close
120
+
121
+ end