diameter 0.1.0.beta

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,167 @@
1
+ require 'uri'
2
+ require 'socket'
3
+ require 'diameter/message'
4
+ require 'diameter/avp'
5
+
6
+ if RUBY_ENGINE != 'jruby'
7
+ ServerSocket = Socket
8
+ end
9
+
10
+ module Diameter
11
+ module Internals
12
+ # @private
13
+ class StackHelper
14
+ def initialize(stack)
15
+ @all_connections = []
16
+ @listen_connections = []
17
+ @data = {}
18
+ @stack = stack
19
+ @loop_thread = nil
20
+ @accept_loop_thread = nil
21
+ @connection_lock = Mutex.new
22
+ @wakeup_pipe_rd, @wakeup_pipe_wr = IO.pipe
23
+ end
24
+
25
+ def start_main_loop
26
+ @loop_thread = Thread.new do
27
+ loop do
28
+ main_loop
29
+ end
30
+ end
31
+ @loop_thread.abort_on_exception = true
32
+ end
33
+
34
+ def wakeup
35
+ @wakeup_pipe_wr.puts "wakeup"
36
+ end
37
+
38
+ def main_loop
39
+ rs, _ws, es = IO.select(@all_connections + [@wakeup_pipe_rd], [], @all_connections)
40
+
41
+ es.each do |e|
42
+ Diameter.logger.log(Logger::WARN, "Exception on connection #{e}")
43
+ end
44
+
45
+ rs.each do |r|
46
+ if r == @wakeup_pipe_rd
47
+ r.gets
48
+ next
49
+ end
50
+
51
+ existing_data = @data[r]
52
+ if existing_data.length < 4
53
+ msg, _src = r.recv_nonblock(4 - existing_data.length)
54
+ if msg == ''
55
+ Diameter.logger.warn('Received 0 bytes on read, closing connection')
56
+ close(r)
57
+ else
58
+ existing_data += msg
59
+ end
60
+ end
61
+
62
+ expected_len = -1
63
+ if existing_data.length >= 4
64
+ expected_len = Message.length_from_header(existing_data[0..4])
65
+ Diameter.logger.debug("Read 4 bytes #{existing_data[0..4].inspect}, " \
66
+ "reading full message of length #{expected_len}")
67
+ msg, _src = r.recv_nonblock(expected_len - existing_data.length)
68
+ existing_data += msg
69
+ if msg == ''
70
+ # Connection closed
71
+ Diameter.logger.warn('Received 0 bytes on read, closing connection')
72
+ close(r)
73
+ end
74
+ end
75
+
76
+ if existing_data.length == expected_len
77
+ @stack.handle_message(existing_data, r)
78
+ @data[r] = ''
79
+ else
80
+ @data[r] = existing_data
81
+ end
82
+ end
83
+ end
84
+
85
+ def send(bytes, connection)
86
+ connection.send(bytes, 0)
87
+ end
88
+ end
89
+
90
+ # @private
91
+ class TCPStackHelper < StackHelper
92
+ def setup_new_connection(host, port)
93
+ sd = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
94
+ sd.connect(Socket.pack_sockaddr_in(port, host))
95
+ @all_connections.push sd
96
+ @data[sd] = ''
97
+ wakeup
98
+ sd
99
+ end
100
+
101
+ def shutdown
102
+ @accept_loop_thread.kill if @accept_loop_thread
103
+ @loop_thread.kill if @loop_thread
104
+
105
+ @all_connections.each { |c| close(c) }
106
+ @listen_connections.each { |c| close(c) }
107
+ @all_connections = []
108
+ @listen_connections = []
109
+ end
110
+
111
+ def close(connection)
112
+ connection.close
113
+ @all_connections.delete connection
114
+ @listen_connections.delete connection
115
+ @data.delete connection
116
+ rescue IOError
117
+ # It's OK if this is already closed
118
+ end
119
+
120
+ def setup_new_listen_connection(host, port)
121
+ sd = ServerSocket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
122
+ # reuse = [1,0].pack('ii')
123
+ sd.setsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEADDR, true)
124
+ sd.bind(Socket.pack_sockaddr_in(port, host))
125
+ sd.listen(100)
126
+ @listen_connections.push sd
127
+ @accept_loop_thread = Thread.new do
128
+ loop do
129
+ accept_loop
130
+ end
131
+ end
132
+ @accept_loop_thread.abort_on_exception = true
133
+ end
134
+
135
+ def accept_loop
136
+ rs, _ws, es = IO.select(@listen_connections, [], @listen_connections)
137
+ es.each do |e|
138
+ Diameter.logger.log(Logger::WARN, "Exception on connection #{e}")
139
+ end
140
+
141
+ rs.each do |r|
142
+ sock, addr = r.accept
143
+ Diameter.logger.debug("Accepting connection from #{addr}")
144
+ @data[sock] = ''
145
+ @all_connections.push sock
146
+ wakeup
147
+ end
148
+ end
149
+ end
150
+
151
+ =begin
152
+ # @private
153
+ class SCTPStackHelper
154
+ def setup_new_connection(host, port)
155
+ sd = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
156
+ sd.connect(Socket.pack_sockaddr_in(port, host))
157
+ end
158
+
159
+ def setup_new_listen_connection(_host, _port)
160
+ end
161
+
162
+ def send(_bytes, _connection)
163
+ end
164
+ end
165
+ =end
166
+ end
167
+ end
@@ -0,0 +1,36 @@
1
+ module Diameter
2
+ module Internals
3
+
4
+ # @private
5
+ # Methods for handling 24-bit unsigned integers, used for length and
6
+ # Command-Codes but not representable by String#unpack or Array#pack.
7
+ module UInt24
8
+ # @api private
9
+ #
10
+ # Generates an unsigned integer from two other unsigned integers
11
+ # (one representing the top 8 bits, one representing the bottom 16
12
+ # bits).
13
+ #
14
+ # @param eightb [Fixnum] The top 8 bits (max 255)
15
+ # @param sixteenb [Fixnum] The low 16 bits (max 2^16-1)
16
+ # @return [Fixnum] (max 2^24-1)
17
+ def self.from_u8_and_u16(eightb, sixteenb)
18
+ (eightb << 16) + sixteenb
19
+ end
20
+
21
+ # @api private
22
+ #
23
+ # Converts an unsigned integer to two other unsigned integers (one
24
+ # representing the top 8 bits, one representing the bottom 16 bits).
25
+ #
26
+ # @param twentyfourb [Fixnum] The original number (max 2^24-1)
27
+ # @return [[Fixnum, Fixnum]] Separate integers representing the
28
+ # top 8 and low 16 bits.
29
+ def self.to_u8_and_u16(twentyfourb)
30
+ top_eight = twentyfourb >> 16
31
+ bottom_sixteen = twentyfourb - (top_eight << 16)
32
+ [top_eight, bottom_sixteen]
33
+ end
34
+ end
35
+ end
36
+ end
data/lib/diameter.rb ADDED
@@ -0,0 +1,2 @@
1
+ require 'diameter/stack'
2
+ require 'diameter/message'
metadata ADDED
@@ -0,0 +1,139 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: diameter
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0.beta
5
+ platform: ruby
6
+ authors:
7
+ - Rob Day
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-12-15 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: concurrent-ruby
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.7'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0.7'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rubocop
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '0.28'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '0.28'
41
+ - !ruby/object:Gem::Dependency
42
+ name: yard
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '='
46
+ - !ruby/object:Gem::Version
47
+ version: '0.8'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '='
53
+ - !ruby/object:Gem::Version
54
+ version: '0.8'
55
+ - !ruby/object:Gem::Dependency
56
+ name: simplecov
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '='
60
+ - !ruby/object:Gem::Version
61
+ version: '0.9'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '='
67
+ - !ruby/object:Gem::Version
68
+ version: '0.9'
69
+ - !ruby/object:Gem::Dependency
70
+ name: mocha
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '='
74
+ - !ruby/object:Gem::Version
75
+ version: '1.1'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '='
81
+ - !ruby/object:Gem::Version
82
+ version: '1.1'
83
+ - !ruby/object:Gem::Dependency
84
+ name: minitest-spec-context
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - '='
88
+ - !ruby/object:Gem::Version
89
+ version: 0.0.3
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - '='
95
+ - !ruby/object:Gem::Version
96
+ version: 0.0.3
97
+ description:
98
+ email: ruby-diameter@rkd.me.uk
99
+ executables: []
100
+ extensions: []
101
+ extra_rdoc_files: []
102
+ files:
103
+ - lib/diameter.rb
104
+ - lib/diameter/avp.rb
105
+ - lib/diameter/avp_parser.rb
106
+ - lib/diameter/constants.rb
107
+ - lib/diameter/diameter_logger.rb
108
+ - lib/diameter/fsm.rb
109
+ - lib/diameter/message.rb
110
+ - lib/diameter/peer.rb
111
+ - lib/diameter/stack.rb
112
+ - lib/diameter/stack_transport_helpers.rb
113
+ - lib/diameter/u24.rb
114
+ homepage: http://rkday.github.io/ruby-diameter/api-docs/master/
115
+ licenses:
116
+ - MIT
117
+ metadata: {}
118
+ post_install_message:
119
+ rdoc_options: []
120
+ require_paths:
121
+ - lib
122
+ required_ruby_version: !ruby/object:Gem::Requirement
123
+ requirements:
124
+ - - ">="
125
+ - !ruby/object:Gem::Version
126
+ version: '0'
127
+ required_rubygems_version: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">"
130
+ - !ruby/object:Gem::Version
131
+ version: 1.3.1
132
+ requirements: []
133
+ rubyforge_project:
134
+ rubygems_version: 2.4.3
135
+ signing_key:
136
+ specification_version: 4
137
+ summary: Pure-Ruby Diameter stack
138
+ test_files: []
139
+ has_rdoc: