kafkr 0.9.1 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c15f14feed8ee398816eb4454850798f764f10cafdf12f27aca353ac2412fa1b
4
- data.tar.gz: 77c4facf9f2290d087d2b5b40aa63714e0c01f5d0f4a33cce4c85349264dfe63
3
+ metadata.gz: 3a404654568d9f0c4a16e16f2341757b231cefd27e02186a4de5e34e945afe07
4
+ data.tar.gz: 3acf9756a6d8ff3f372d595171cb967736430768c6181b7aca9e79f44061bc8f
5
5
  SHA512:
6
- metadata.gz: 3e1089f1fbe70c98be8c9ec09fe9d9967c20594c120857fa486bde44bb3ab2ef32cb02df7ae485e383ac1d802243a9468019bf9e6bfd6e057ce57fe14e465e4a
7
- data.tar.gz: e929ce1c00207dd3f63be0294f73d4b885a3786a0748e84650baedd3a01e22e9755de5acf85d111d8e0ea29ce0ba1d4e51906f5a5228c0b5b2acbb3de56857a8
6
+ metadata.gz: b7ca82215482129450e112709ed46533be1638d525b433d92d571b188f93d01c787be4a4eb31cad4509d7c96df23850795bf77171972178491c2ed850868a2e0
7
+ data.tar.gz: 7bb54ca0f2ac1fdd3a4db32c12f8e90bc70dfd272007b9b17204a177d488788f5c3c4430a2f12920e07c614f92deac0cd8d3b4a007ee4fc12253dea4b0efe447
data/.DS_Store ADDED
Binary file
@@ -39,7 +39,6 @@ module Kafkr
39
39
  end
40
40
 
41
41
  def load_handlers(directory = "./handlers")
42
- # Load handlers and check for new additions
43
42
  Dir.glob("#{directory}/*.rb").each do |file|
44
43
  handler_name = File.basename(file, ".rb")
45
44
  unless $loaded_handlers[handler_name]
@@ -48,11 +47,6 @@ module Kafkr
48
47
  $handlers_changed = true
49
48
  end
50
49
  end
51
-
52
- # Display handlers if there are changes
53
- if $handlers_changed
54
- $handlers_changed = false
55
- end
56
50
  end
57
51
  end
58
52
 
@@ -65,20 +59,19 @@ module Kafkr
65
59
  raise NotImplementedError, "You must implement the handle method"
66
60
  end
67
61
 
68
- # ... rest of your existing Handler methods ...
69
62
  def self.inherited(subclass)
70
63
  Consumer.register_handler(subclass.new)
71
64
  end
72
65
 
73
66
  protected
74
67
 
75
- def reply to:, payload:
68
+ def reply(to:, payload:)
76
69
  Kafkr::Producer.configure do |config|
77
70
  config.host = Consumer.configuration.host
78
71
  config.port = Consumer.configuration.port
79
72
  end
80
73
 
81
- Kafkr::Producer.send_message({reply: {payload: payload, uuid: to["sync_uid"]}}, acknowledge: false)
74
+ Kafkr::Producer.send_message({reply: {payload: payload, uuid: to["sync_uid"]}})
82
75
  end
83
76
 
84
77
  private
@@ -114,45 +107,34 @@ module Kafkr
114
107
  end
115
108
 
116
109
  def valid_class_name?(name)
117
- # A valid class name starts with an uppercase letter and
118
- # followed by zero or more letters, numbers, or underscores.
119
110
  /^[A-Z]\w*$/.match?(name)
120
111
  end
121
112
 
122
- # sugests a working handler
123
113
  def print_handler_class(name)
124
114
  return if name.is_a?(Numeric)
125
-
126
- # If name is a Hash, use its first key
127
115
  name = name.keys.first if name.is_a?(Hash)
128
-
129
- # Generate the handler name based on the naming convention
130
116
  handler_name = "#{name.downcase}_handler"
131
117
 
132
- # Check if the handler is already loaded
133
118
  if $loaded_handlers.key?(handler_name)
134
119
  return
135
120
  end
136
121
 
137
122
  if Kafkr::Consumer.configuration.suggest_handlers
138
- if valid_class_name? name.capitalize
139
- puts "No handler for this message, you could use this one."
140
- puts ""
123
+ if valid_class_name?(name.capitalize)
124
+ puts "No handler for this message, you could use this one.\n\n"
141
125
 
142
126
  handler_class_string = <<~HANDLER_CLASS
143
-
144
127
  class #{name.capitalize}Handler < Kafkr::Consumer::Handler
145
128
  def handle?(message)
146
129
  can_handle? message, '#{name}'
147
130
  end
148
-
131
+
149
132
  def handle(message)
150
133
  puts message
151
134
  end
152
135
  end
153
136
 
154
- save the file to ./handlers/#{name}_handler.rb
155
-
137
+ # Save the file to ./handlers/#{name}_handler.rb
156
138
  HANDLER_CLASS
157
139
 
158
140
  puts handler_class_string
@@ -165,38 +147,28 @@ module Kafkr
165
147
  def listen_for(message, send_message)
166
148
  attempt = 0
167
149
  begin
168
- socket = TCPSocket.new(Consumer.configuration.host, Consumer.configuration.port)
150
+ socket = TCPSocket.new(@host, @port)
169
151
  attempt = 0
170
152
 
171
153
  Timeout.timeout(20) do
172
- # Call the provided send_message method or lambda, passing the message as an argument
173
- sync_uid = send_message.call(message, acknowledge: false)
154
+ sync_uid = send_message.call(message)
174
155
 
175
156
  loop do
176
157
  received_message = socket.gets
177
158
  raise LostConnection if received_message.nil?
178
- # Assuming Kafkr::Encryptor is defined elsewhere
179
159
  received_message = Kafkr::Encryptor.new.decrypt(received_message.chomp)
180
- # Yield every received message to the given block
181
160
  if valid_json?(received_message)
182
161
  payload = yield JSON.parse(received_message), sync_uid if block_given?
183
162
  return payload if payload
184
163
  end
185
164
  end
186
165
  end
187
- rescue Timeout::Error
188
- puts "Listening timed out after 20 seconds."
189
- socket&.close
190
- rescue LostConnection
191
- attempt += 1
192
- wait_time = backoff_time(attempt)
193
- puts "Connection lost. Reconnecting in #{wait_time} seconds..."
194
- sleep(wait_time)
195
- rescue Errno::ECONNREFUSED, Timeout::Error
166
+ rescue Timeout::Error, LostConnection, Errno::ECONNREFUSED
196
167
  attempt += 1
197
168
  wait_time = backoff_time(attempt)
198
- puts "Failed to connect on attempt #{attempt}. Retrying in #{wait_time} seconds..."
169
+ puts "Attempt #{attempt}: Retrying in #{wait_time} seconds..."
199
170
  sleep(wait_time)
171
+ retry
200
172
  rescue Interrupt
201
173
  puts "Received interrupt signal. Shutting down consumer gracefully..."
202
174
  socket&.close
@@ -207,39 +179,9 @@ module Kafkr
207
179
  def listen
208
180
  attempt = 0
209
181
  loop do
210
- socket = TCPSocket.new(Consumer.configuration.host, Consumer.configuration.port)
211
- attempt = 0
212
-
213
- loop do
214
- message = socket.gets
215
- raise LostConnection if message.nil?
216
-
217
- # Assuming Kafkr::Encryptor is defined elsewhere
218
- message = Kafkr::Encryptor.new.decrypt(message.chomp)
219
- if valid_json?(message)
220
- dispatch_to_handlers(JSON.parse(message)) do |message|
221
- yield message if block_given?
222
- end
223
- else
224
- dispatch_to_handlers(message) do |message|
225
- yield message if block_given?
226
- end
227
- end
182
+ listen_for("dummy", ->(msg) { puts "Listening..." }) do |message|
183
+ puts "Received message: #{message}"
228
184
  end
229
- rescue LostConnection
230
- attempt += 1
231
- wait_time = backoff_time(attempt)
232
- puts "Connection lost. Reconnecting in #{wait_time} seconds..."
233
- sleep(wait_time)
234
- rescue Errno::ECONNREFUSED, Timeout::Error
235
- attempt += 1
236
- wait_time = backoff_time(attempt)
237
- puts "Failed to connect on attempt #{attempt}. Retrying in #{wait_time} seconds..."
238
- sleep(wait_time)
239
- rescue Interrupt
240
- puts "Received interrupt signal. Shutting down consumer gracefully..."
241
- socket&.close
242
- exit(0)
243
185
  end
244
186
  end
245
187
 
@@ -273,6 +215,5 @@ module Kafkr
273
215
  end
274
216
  end
275
217
 
276
- # Assuming the handlers directory is the default location
277
218
  Consumer.load_handlers
278
219
  end
@@ -6,7 +6,7 @@ module Kafkr
6
6
  ALGORITHM = "aes-256-cbc"
7
7
 
8
8
  def initialize
9
- @key = Base64.decode64("2wZ85yxQe0lmiQ5nsqdmPWoGB0W6HZW8S/UXVTLQ6WY=")
9
+ @key = Base64.decode64(ENV["KAFKR_ENCRYPTION_KEY"])
10
10
  end
11
11
 
12
12
  def encrypt(data)
data/lib/kafkr/log.rb CHANGED
@@ -8,22 +8,17 @@ module Kafkr
8
8
  @received_file = "./.kafkr/log.txt"
9
9
  @broker = MessageBroker.new
10
10
  @whitelist = load_whitelist
11
- @acknowledged_message_ids = load_acknowledged_message_ids
12
11
  end
13
12
 
14
- def load_acknowledged_message_ids
15
- unless File.exist?("./.kafkr/acknowledged_message_ids.txt")
16
- `mkdir -p ./.kafkr`
17
- `touch ./.kafkr/acknowledged_message_ids.txt`
13
+ def load_whitelist
14
+ whitelist = ["localhost", "::1", "127.0.0.1"]
15
+ if File.exist?("whitelist.txt")
16
+ File.readlines("whitelist.txt").each do |line|
17
+ ip = line.strip.sub(/^::ffff:/, "")
18
+ whitelist << ip
19
+ end
18
20
  end
19
-
20
- config_path = File.expand_path("./.kafkr/acknowledged_message_ids.txt")
21
- return [] unless File.exist?(config_path)
22
-
23
- File.readlines(config_path).map(&:strip)
24
- rescue Errno::ENOENT, Errno::EACCES => e
25
- puts "Error loading acknowledged_message_ids: #{e.message}"
26
- []
21
+ whitelist
27
22
  end
28
23
 
29
24
  def start
@@ -53,14 +48,7 @@ module Kafkr
53
48
  message = decryptor.decrypt(encrypted_message.chomp) # Decrypt the message here
54
49
  uuid, message_content = extract_uuid(message)
55
50
  if uuid && message_content
56
- if @acknowledged_message_ids.include?(uuid)
57
- acknowledge_existing_message(uuid, client)
58
- else
59
- acknowledge_message(uuid, client)
60
- persist_received_message(uuid)
61
- @acknowledged_message_ids << uuid
62
- @broker.broadcast(message_content)
63
- end
51
+ @broker.broadcast(message_content)
64
52
  else
65
53
  puts "Received invalid message format: #{message}"
66
54
  end
@@ -73,17 +61,6 @@ module Kafkr
73
61
  end
74
62
  end
75
63
 
76
- def load_whitelist
77
- whitelist = ["localhost", "::1", "127.0.0.1"]
78
- if File.exist?("whitelist.txt")
79
- File.readlines("whitelist.txt").each do |line|
80
- ip = line.strip.sub(/^::ffff:/, "")
81
- whitelist << ip
82
- end
83
- end
84
- whitelist
85
- end
86
-
87
64
  def whitelisted?(ip)
88
65
  @whitelist.include?(ip.gsub("::ffff:", ""))
89
66
  end
@@ -91,39 +68,15 @@ module Kafkr
91
68
  private
92
69
 
93
70
  def extract_uuid(message)
94
-
95
- #check if message if valid json
71
+ # Check if message is valid JSON
96
72
  begin
97
73
  message = JSON.parse(message)
98
-
99
74
  return message["uuid"], message
100
-
101
75
  rescue JSON::ParserError => e
102
76
  puts "Received invalid message format: #{message}"
103
77
  match_data = /^(\w{8}-\w{4}-\w{4}-\w{4}-\w{12}): (.+)$/.match(message)
104
78
  match_data ? [match_data[1], match_data[2]] : [nil, nil]
105
79
  end
106
-
107
- end
108
-
109
- def acknowledge_message(uuid, client)
110
- puts "Received message with UUID #{uuid}. Acknowledged."
111
- acknowledgment_message = "ACK: #{uuid}"
112
- client.puts(acknowledgment_message)
113
- puts "Acknowledgment sent to producer: #{acknowledgment_message}"
114
- end
115
-
116
- def acknowledge_existing_message(uuid, client)
117
- puts "Received duplicate message with UUID #{uuid}. Already Acknowledged."
118
- acknowledgment_message = "ACK-DUPLICATE: #{uuid}"
119
- client.puts(acknowledgment_message)
120
- puts "Duplicate acknowledgment sent to producer: #{acknowledgment_message}"
121
- end
122
-
123
- def persist_received_message(uuid)
124
- File.open("./.kafkr/acknowledged_message_ids.txt", "a") do |file|
125
- file.puts(uuid)
126
- end
127
80
  end
128
81
  end
129
82
  end
@@ -10,16 +10,12 @@ module Kafkr
10
10
  @@file_mutex = Mutex.new
11
11
 
12
12
  MESSAGE_QUEUE = "./.kafkr/message_queue.txt"
13
- ACKNOWLEDGED_MESSAGE_QUEUE = "./.kafkr/acknowledged_messages.txt"
14
13
 
15
14
  def self.configuration
16
15
  FileUtils.mkdir_p "./.kafkr"
17
16
  @configuration ||= OpenStruct.new
18
17
  @configuration.queue_file = MESSAGE_QUEUE
19
- @configuration.acknowledged_file = ACKNOWLEDGED_MESSAGE_QUEUE
20
18
  @configuration.message_queue = []
21
- @configuration.acknowledged_messages = []
22
- @configuration.acknowledged_messages = load_acknowledged_messages
23
19
  load_queue_from_file
24
20
  @configuration.is_json = false
25
21
  @configuration
@@ -32,48 +28,29 @@ module Kafkr
32
28
  end
33
29
 
34
30
  def self.structured_data_to_hash(input:, sync_uid:)
35
- # Check the overall structure with regex and make quotes optional
36
31
  unless /\A\w+\s*(=>|<=>)\s*((\w+:\s*['"]?[^'",]*['"]?,\s*)*(\w+:\s*['"]?[^'",]*['"]?)\s*)\z/.match?(input)
37
32
  return input
38
33
  end
39
34
 
40
35
  if input.include?("<=>")
41
- # puts "sync message"
42
- # Extract the type and key-value pairs
43
36
  type, key_values_str = input.split("<=>").map(&:strip)
44
-
45
- # puts type
46
- # puts key_values_str
47
-
48
37
  key_values = key_values_str.scan(/(\w+):\s*['"]?([^'",]*)['"]?/)
49
-
50
- # Convert the array of pairs into a hash, stripping quotes if they exist
51
38
  hash_body = key_values.to_h do |key, value|
52
39
  [key.to_sym, value.strip.gsub(/\A['"]|['"]\z/, "")]
53
40
  end
54
-
55
- # Return the final hash with the type as the key
56
41
  {type.to_sym => hash_body, :sync => true, :sync_uid => sync_uid}
57
-
58
42
  else
59
- # puts "async message"
60
- # Extract the type and key-value pairs
61
43
  type, key_values_str = input.split("=>").map(&:strip)
62
44
  key_values = key_values_str.scan(/(\w+):\s*['"]?([^'",]*)['"]?/)
63
-
64
- # Convert the array of pairs into a hash, stripping quotes if they exist
65
45
  hash_body = key_values.to_h do |key, value|
66
46
  [key.to_sym, value.strip.gsub(/\A['"]|['"]\z/, "")]
67
47
  end
68
-
69
- # Return the final hash with the type as the key
70
48
  {type.to_sym => hash_body}
71
49
  end
72
50
  end
73
51
 
74
- def self.send_message(message, acknowledge: true)
52
+ def self.send_message(message)
75
53
  uuid = SecureRandom.uuid
76
-
77
54
  message_with_uuid = nil
78
55
 
79
56
  if Kafkr::Producer.configuration.is_json
@@ -91,28 +68,14 @@ module Kafkr
91
68
  end
92
69
  end
93
70
 
94
- # Encrypt the message here
95
71
  encrypted_message_with_uuid = Kafkr::Encryptor.new.encrypt(message_with_uuid)
96
72
 
97
73
  begin
98
- if acknowledge
99
- if !@configuration.acknowledged_messages.include?(uuid)
100
- socket = TCPSocket.new(@configuration.host, @configuration.port)
101
- listen_for_acknowledgments(socket) if acknowledge
102
- send_queued_messages(socket)
103
- # Send the encrypted message instead of the plain one
104
- socket.puts(encrypted_message_with_uuid)
105
- else
106
- puts "Message with UUID #{uuid} has already been acknowledged. Skipping."
107
- end
108
- else
109
- socket = TCPSocket.new(@configuration.host, @configuration.port)
110
- send_queued_messages(socket)
111
- socket.puts(encrypted_message_with_uuid)
112
- end
74
+ socket = TCPSocket.new(@configuration.host, @configuration.port)
75
+ send_queued_messages(socket)
76
+ socket.puts(encrypted_message_with_uuid)
113
77
  rescue Errno::ECONNREFUSED
114
78
  puts "Connection refused. Queuing message: #{encrypted_message_with_uuid}"
115
- # Queue the encrypted message
116
79
  @configuration.message_queue.push(encrypted_message_with_uuid)
117
80
  save_queue_to_file
118
81
  rescue Errno::EPIPE
@@ -124,36 +87,15 @@ module Kafkr
124
87
  end
125
88
 
126
89
  def self.send_message_and_wait(message)
127
- # Using method(:send_message) to pass the send_message method as a callable object
128
-
129
90
  Consumer.new.listen_for(message, method(:send_message)) do |received_message, sync_uid|
130
- if received_message.key? "reply"
131
- if received_message["reply"].dig("uuid") == sync_uid
132
- received_message["reply"].dig("payload")
133
- end
91
+ if received_message.key? "reply" and received_message["reply"].dig("uuid") == sync_uid
92
+ received_message["reply"].dig("payload")
134
93
  end
135
94
  end
136
95
  end
137
96
 
138
97
  private
139
98
 
140
- def self.listen_for_acknowledgments(socket)
141
- Thread.new do
142
- while line = socket.gets
143
- line = line.chomp
144
- if line.start_with?("ACK:")
145
- uuid = line.split(" ")[1]
146
- handle_acknowledgment(uuid)
147
- end
148
- end
149
- end
150
- end
151
-
152
- def self.handle_acknowledgment(uuid)
153
- @configuration.acknowledged_messages << uuid
154
- save_acknowledged_messages
155
- end
156
-
157
99
  def self.retry_connection(message_with_uuid)
158
100
  sleep(5)
159
101
  send_message(message_with_uuid)
@@ -182,24 +124,6 @@ module Kafkr
182
124
  end
183
125
  end
184
126
 
185
- def self.load_acknowledged_messages
186
- @@file_mutex.synchronize do
187
- if File.exist?(@configuration.acknowledged_file)
188
- File.readlines(@configuration.acknowledged_file).map(&:chomp)
189
- else
190
- []
191
- end
192
- end
193
- end
194
-
195
- def self.save_acknowledged_messages
196
- @@file_mutex.synchronize do
197
- File.open(@configuration.acknowledged_file, "w") do |file|
198
- file.puts(@configuration.acknowledged_messages)
199
- end
200
- end
201
- end
202
-
203
127
  def self.logger
204
128
  @logger ||= Logger.new(STDOUT)
205
129
  end
data/lib/kafkr/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Kafkr
4
- VERSION = "0.9.1"
4
+ VERSION = "0.10.0"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kafkr
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.1
4
+ version: 0.10.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Delaney Kuldvee Burke
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-01-25 00:00:00.000000000 Z
11
+ date: 2024-04-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: gibberish
@@ -35,6 +35,7 @@ executables:
35
35
  extensions: []
36
36
  extra_rdoc_files: []
37
37
  files:
38
+ - ".DS_Store"
38
39
  - ".rspec"
39
40
  - ".standard.yml"
40
41
  - README.md
@@ -71,7 +72,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
71
72
  - !ruby/object:Gem::Version
72
73
  version: '0'
73
74
  requirements: []
74
- rubygems_version: 3.5.3
75
+ rubygems_version: 3.5.5
75
76
  signing_key:
76
77
  specification_version: 4
77
78
  summary: A homage to kafkr implmented in ruby