relp 0.1.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.
- checksums.yaml +7 -0
- data/LICENSE.txt +21 -0
- data/README.md +77 -0
- data/Rakefile +11 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/relp.rb +4 -0
- data/lib/relp/client.rb +4 -0
- data/lib/relp/exceptions.rb +16 -0
- data/lib/relp/relp_protocol.rb +58 -0
- data/lib/relp/server.rb +159 -0
- data/lib/relp/version.rb +3 -0
- data/test/RELP_test.rb +9 -0
- data/test/test_helper.rb +4 -0
- metadata +102 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 3c892d37fbb5525fd7a3c19f19b81528792c0ab2
|
4
|
+
data.tar.gz: 89bf3ac784ede6f01be3ae2a9d97dc3352ecefd5
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 1bfc8c082854df95a56dfc5cf12c72c8f299e5a8285f127aaec3caf074c7b9777177fd0875b62edc5651698b0744bd51987fe0c527b3a534af160c83b4575e0a
|
7
|
+
data.tar.gz: 5de5ee3efb069ddd01dcf6ca9885a5ae94b4199181d95446eb3fed9387afe2b1f8e06ecf262b6908948a8f49e32591a398eb7e4895533d9cf85e05d97500dca7
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2017 dhlavac
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
# RELP
|
2
|
+
|
3
|
+
This library contains native implementation of [RELP protocol](http://www.rsyslog.com/doc/relp.html) in ruby. At the moment only server-side
|
4
|
+
is properly implemented and (to some extent) tested.
|
5
|
+
|
6
|
+
## Installation
|
7
|
+
|
8
|
+
Add this line to your application's Gemfile:
|
9
|
+
|
10
|
+
```ruby
|
11
|
+
gem 'relp'
|
12
|
+
```
|
13
|
+
|
14
|
+
And then execute:
|
15
|
+
|
16
|
+
$ bundle
|
17
|
+
|
18
|
+
Or install it yourself as:
|
19
|
+
|
20
|
+
$ gem install relp
|
21
|
+
|
22
|
+
## Usage
|
23
|
+
|
24
|
+
### Server
|
25
|
+
|
26
|
+
To run server just creat instance of `Relp::RelpServer.new(host, port, logger = nil, callback)`
|
27
|
+
and than call method run on instance of server e.g. `server.run`
|
28
|
+
|
29
|
+
`host`
|
30
|
+
* This is a required setting.
|
31
|
+
* Value type is string
|
32
|
+
* There is no default value for this setting.
|
33
|
+
* Specifies address you want to bind to, use "0.0.0.0" to bind to any address
|
34
|
+
|
35
|
+
|
36
|
+
|
37
|
+
`port`
|
38
|
+
|
39
|
+
* This is a required setting.
|
40
|
+
* Value type is number
|
41
|
+
* There is no default value for this setting.
|
42
|
+
* Sets on which port you want to listen for incoming RELP connections
|
43
|
+
|
44
|
+
|
45
|
+
`logger`
|
46
|
+
|
47
|
+
* This is optional setting
|
48
|
+
* Value type is logger object
|
49
|
+
* If is not set - default is `Logger.new(STDOUT)` with all levels of logging
|
50
|
+
|
51
|
+
`callback`
|
52
|
+
* This is a required setting.
|
53
|
+
* Method you want to be executed upon successfully accepted message, it has only one :string parameter, which is message itself.
|
54
|
+
|
55
|
+
####Important Methods
|
56
|
+
* `run` Start connceting clients
|
57
|
+
* `server_shutdown` Close connection to all clients and shutdown server
|
58
|
+
|
59
|
+
### Client
|
60
|
+
|
61
|
+
Coming soon.
|
62
|
+
|
63
|
+
|
64
|
+
## Development
|
65
|
+
|
66
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
67
|
+
|
68
|
+
To install this gem onto your local machine, run `bundle exec rake install`.
|
69
|
+
## Contributing
|
70
|
+
|
71
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/dhlavac/RELP.
|
72
|
+
|
73
|
+
|
74
|
+
## License
|
75
|
+
|
76
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
77
|
+
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "relp"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start
|
data/bin/setup
ADDED
data/lib/relp.rb
ADDED
data/lib/relp/client.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
module Relp
|
2
|
+
class RelpProtocolError < StandardError
|
3
|
+
end
|
4
|
+
|
5
|
+
class ConnectionClosed < RelpProtocolError
|
6
|
+
end
|
7
|
+
|
8
|
+
class ConnectionRefused < RelpProtocolError
|
9
|
+
end
|
10
|
+
|
11
|
+
class FrameReadException < RelpProtocolError
|
12
|
+
end
|
13
|
+
|
14
|
+
class InvalidCommand < RelpProtocolError
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'relp/exceptions'
|
2
|
+
require 'socket'
|
3
|
+
module Relp
|
4
|
+
class RelpProtocol
|
5
|
+
@@relp_version = '0'
|
6
|
+
@@relp_software = 'librelp,1.2.13,http://librelp.adiscon.com'
|
7
|
+
def frame_write(socket, frame)
|
8
|
+
raw_data=[
|
9
|
+
frame[:txnr],
|
10
|
+
frame[:command],
|
11
|
+
frame[:message]
|
12
|
+
].join(' ')
|
13
|
+
@logger.debug "Writing Frame #{frame.inspect}"
|
14
|
+
begin
|
15
|
+
socket.write(raw_data)
|
16
|
+
rescue Errno::EPIPE,IOError,Errno::ECONNRESET
|
17
|
+
raise Relp::ConnectionClosed
|
18
|
+
end
|
19
|
+
end
|
20
|
+
# Read socket and return Relp frame information in hash
|
21
|
+
def frame_read(socket)
|
22
|
+
begin
|
23
|
+
socket_content = socket.read_nonblock(4096)
|
24
|
+
frame = Hash.new
|
25
|
+
if match = socket_content.match(/(^[0-9]+) ([\S]*) (\d+)([\s\S]*)/)
|
26
|
+
frame[:txnr], frame[:command], frame[:data_length], frame[:message] = match.captures
|
27
|
+
frame[:message].lstrip! #message could be empty
|
28
|
+
else
|
29
|
+
raise raise Relp::FrameReadException.new('Problem with reading RELP frame')
|
30
|
+
end
|
31
|
+
@logger.debug "Reading Frame #{frame.inspect}"
|
32
|
+
rescue IOError
|
33
|
+
@logger.error 'Problem with reading RELP frame'
|
34
|
+
raise Relp::FrameReadException.new 'Problem with reading RELP frame'
|
35
|
+
rescue Errno::ECONNRESET
|
36
|
+
@logger.error 'Connection reset'
|
37
|
+
raise Relp::ConnectionClosed.new 'Connection closed'
|
38
|
+
end
|
39
|
+
is_valid_command(frame[:command])
|
40
|
+
|
41
|
+
return frame
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
# Check if command is one of valid commands if not raise exception
|
46
|
+
def is_valid_command(command)
|
47
|
+
valid_commands = ["open", "close", "rsp", "syslog"]
|
48
|
+
if !valid_commands.include?(command)
|
49
|
+
@logger.error 'Invalid RELP command'
|
50
|
+
raise Relp::InvalidCommand.new('Invalid command')
|
51
|
+
end
|
52
|
+
end
|
53
|
+
# Parse information from message and crate new hash (symbol => value) e.g. (:version => 0)
|
54
|
+
def extract_message_information(message)
|
55
|
+
informations = Hash[message.scan(/^(.*)=(.*)$/).map { |(key, value)| [key.to_sym, value] }]
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
data/lib/relp/server.rb
ADDED
@@ -0,0 +1,159 @@
|
|
1
|
+
require 'relp/relp_protocol'
|
2
|
+
require 'logger'
|
3
|
+
require 'thread'
|
4
|
+
|
5
|
+
module Relp
|
6
|
+
class RelpServer < RelpProtocol
|
7
|
+
def initialize(host, port, logger = nil, callback)
|
8
|
+
@logger = logger
|
9
|
+
@logger = Logger.new(STDOUT) if logger.nil?
|
10
|
+
@socket_list = Array.new
|
11
|
+
@callback = callback
|
12
|
+
@required_command = 'syslog'
|
13
|
+
|
14
|
+
begin
|
15
|
+
@server = TCPServer.new(host, port)
|
16
|
+
@logger.info "Starting #{self.class} on %s:%i" % @server.local_address.ip_unpack
|
17
|
+
rescue Errno::EADDRINUSE
|
18
|
+
@logger.error "ERROR Could not start relp server: Port #{port} in use"
|
19
|
+
raise Errno::EADDRINUSE
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def run
|
24
|
+
loop do
|
25
|
+
Thread.start(@server.accept) do |client_socket|
|
26
|
+
begin
|
27
|
+
@socket_list.push client_socket
|
28
|
+
remote_ip = client_socket.peeraddr[3]
|
29
|
+
@logger.info "New client connection coming from ip #{remote_ip}"
|
30
|
+
@logger.debug "New client started with object id=#{client_socket.object_id}"
|
31
|
+
connection_setup(client_socket)
|
32
|
+
while Thread.current.alive? do
|
33
|
+
ready = IO.select([client_socket], nil, nil, 10)
|
34
|
+
if ready
|
35
|
+
frame = communication_processing(client_socket)
|
36
|
+
return_message(frame[:message], (@callback))
|
37
|
+
ack_frame(client_socket,frame[:txnr])
|
38
|
+
end
|
39
|
+
end
|
40
|
+
rescue Relp::ConnectionClosed
|
41
|
+
@logger.info "Connection closed"
|
42
|
+
rescue Relp::RelpProtocolError => err
|
43
|
+
@logger.warn 'Relp error: ' + err.class.to_s + ' ' + err.message
|
44
|
+
ensure
|
45
|
+
server_close_message(client_socket) rescue nil
|
46
|
+
@logger.debug "Closing client socket=#{client_socket.object_id}"
|
47
|
+
@logger.info "Client from ip #{remote_ip} closed"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
rescue Errno::EINVAL
|
52
|
+
# Swallowing exception here because it results even from properly closed socket
|
53
|
+
@logger.info "Socket close."
|
54
|
+
end
|
55
|
+
|
56
|
+
def return_message(message, callback)
|
57
|
+
list_of_messages = message.split(/\n+/)
|
58
|
+
list_of_messages.each do |msg|
|
59
|
+
remove = msg.split(": ").first + ": "
|
60
|
+
msg.slice! remove
|
61
|
+
callback.call(msg)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def create_frame(txnr, command, message)
|
66
|
+
frame = {:txnr => txnr,
|
67
|
+
:command => command,
|
68
|
+
:message => message
|
69
|
+
}
|
70
|
+
end
|
71
|
+
|
72
|
+
def ack_frame(socket, txnr)
|
73
|
+
frame = {:txnr => txnr,
|
74
|
+
:command => 'rsp',
|
75
|
+
:message => "6 200 OK\n"
|
76
|
+
}
|
77
|
+
frame_write(socket, frame)
|
78
|
+
end
|
79
|
+
|
80
|
+
def server_close_message(socket)
|
81
|
+
Hash.new frame = {:txnr => 0,
|
82
|
+
:command => 'serverclose',
|
83
|
+
:message => '0'
|
84
|
+
}
|
85
|
+
begin
|
86
|
+
frame_write(socket,frame)
|
87
|
+
@logger.debug 'Server close message send'
|
88
|
+
socket.close
|
89
|
+
@socket_list.delete socket
|
90
|
+
rescue Relp::ConnectionClosed
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def server_shutdown
|
95
|
+
@socket_list.each do |client_socket|
|
96
|
+
if client_socket != nil
|
97
|
+
server_close_message(client_socket)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
@logger.info 'Server shutdown'
|
101
|
+
@server.shutdown
|
102
|
+
@server = nil
|
103
|
+
end
|
104
|
+
|
105
|
+
private
|
106
|
+
def communication_processing(socket)
|
107
|
+
@logger.debug 'Communication processing'
|
108
|
+
frame = frame_read(socket)
|
109
|
+
if frame[:command] == 'syslog'
|
110
|
+
return frame
|
111
|
+
elsif frame[:command] == 'close'
|
112
|
+
response_frame = create_frame(frame[:txnr], "rsp", "0")
|
113
|
+
frame_write(socket,response_frame)
|
114
|
+
@logger.info 'Client send close message'
|
115
|
+
server_close_message(socket)
|
116
|
+
raise Relp::ConnectionClosed
|
117
|
+
else
|
118
|
+
server_close_message(socket)
|
119
|
+
raise Relp::RelpProtocolError, 'Wrong relp command'
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def connection_setup(socket)
|
124
|
+
@logger.debug 'Connection setup'
|
125
|
+
begin
|
126
|
+
read_ready = IO.select([socket], nil, nil, 10)
|
127
|
+
if read_ready
|
128
|
+
frame = frame_read(socket)
|
129
|
+
@logger.debug 'Frame read complete, processing..'
|
130
|
+
if frame[:command] == 'open'
|
131
|
+
@logger.debug 'Client command open'
|
132
|
+
message_informations = extract_message_information(frame[:message])
|
133
|
+
if message_informations[:relp_version].empty?
|
134
|
+
@logger.warn 'Missing RELP version specification'
|
135
|
+
server_close_message(socket)
|
136
|
+
raise Relp::RelpProtocolError
|
137
|
+
elsif @required_command != message_informations[:commands]
|
138
|
+
@logger.warn 'Missing required commands - syslog'
|
139
|
+
Hash.new response_frame = create_frame(frame[:txnr], 'rsp', '20 500 Missing required command ' + @required_command)
|
140
|
+
frame_write(socket, response_frame)
|
141
|
+
server_close_message(socket)
|
142
|
+
raise Relp::InvalidCommand, 'Missing required command'
|
143
|
+
else
|
144
|
+
Hash.new response_frame = create_frame(frame[:txnr], 'rsp', '93 200 OK' + "\n" + 'relp_version=' +@@relp_version + "\n" + 'relp_software=' + @@relp_software + "\n" + 'commands=' + @required_command + "\n")
|
145
|
+
@logger.debug 'Sending response to client'
|
146
|
+
frame_write(socket, response_frame)
|
147
|
+
end
|
148
|
+
else
|
149
|
+
server_close_message(socket)
|
150
|
+
raise Relp::InvalidCommand, frame[:command] + ' expecting open command'
|
151
|
+
end
|
152
|
+
end
|
153
|
+
rescue Relp::RelpProtocolError
|
154
|
+
@logger.debug 'Timed out (no frame to read)'
|
155
|
+
server_close_message(socket)
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
data/lib/relp/version.rb
ADDED
data/test/RELP_test.rb
ADDED
data/test/test_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: relp
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Dominik Hlaváč Ďurán
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-06-22 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.12'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.12'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: minitest
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '5.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '5.0'
|
55
|
+
description: If you want to receive or send message via RELP protocol you can use
|
56
|
+
this gem
|
57
|
+
email:
|
58
|
+
- dhlavacd@redhat.com
|
59
|
+
executables: []
|
60
|
+
extensions: []
|
61
|
+
extra_rdoc_files: []
|
62
|
+
files:
|
63
|
+
- LICENSE.txt
|
64
|
+
- README.md
|
65
|
+
- Rakefile
|
66
|
+
- bin/console
|
67
|
+
- bin/setup
|
68
|
+
- lib/relp.rb
|
69
|
+
- lib/relp/client.rb
|
70
|
+
- lib/relp/exceptions.rb
|
71
|
+
- lib/relp/relp_protocol.rb
|
72
|
+
- lib/relp/server.rb
|
73
|
+
- lib/relp/version.rb
|
74
|
+
- test/RELP_test.rb
|
75
|
+
- test/test_helper.rb
|
76
|
+
homepage: https://github.com/dhlavac/Relp
|
77
|
+
licenses:
|
78
|
+
- MIT
|
79
|
+
metadata: {}
|
80
|
+
post_install_message:
|
81
|
+
rdoc_options: []
|
82
|
+
require_paths:
|
83
|
+
- lib
|
84
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - ">="
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: 2.0.0
|
89
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
90
|
+
requirements:
|
91
|
+
- - ">="
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
requirements: []
|
95
|
+
rubyforge_project:
|
96
|
+
rubygems_version: 2.5.2
|
97
|
+
signing_key:
|
98
|
+
specification_version: 4
|
99
|
+
summary: Ruby implementation of RELP (Reliable Event Logging Protocol) protocol.
|
100
|
+
test_files:
|
101
|
+
- test/RELP_test.rb
|
102
|
+
- test/test_helper.rb
|