easel-dashboard 0.5 → 0.6

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 45b20ce9712ac3e87e83e527b03665b9c64a9b707f571cfcbbd1c0989df494e8
4
- data.tar.gz: bebb6eeafa225d2a05401fea5f8f8ec7e63674de617a811ec8b6cb2cf7a56640
3
+ metadata.gz: 1bb0d5d2b352f523eaea5641d64b835153d8fe24e2fea68fc493636c207cc25b
4
+ data.tar.gz: 378c2dad0c4ad34d39b908d49b5a32ff779c7a7c26ffc4acc886226b58fb9c51
5
5
  SHA512:
6
- metadata.gz: c50fd692b5fcd3826cc6b3ac80aa400e1319501303f951b576c9c4ada4fe43990a383ed7f84e70d2808363a77dd04e1f07e6b0a2218608309bbed2681a42382b
7
- data.tar.gz: d1353d563bf9b9306ebbf5bb69e7a4b401558b4be77010320b7b734808e74124bdbfaecd12890a61a400a2432ee5d16536c909fa9c4c6f2d13885a488465f788
6
+ metadata.gz: df5b92218548203a7268852978a8238a26709506ee3f7eaf8c7345b6baab6b47b4bdbf786797a04b09815fc70386cf86b46ae9dc6efdda44d1038591efb33521
7
+ data.tar.gz: d2baa673d372e3e86e38635a425c757698e1ca76eb69822725d78ccea2749cd3ffaef804e2331765239797057a127e10b48efe416d2d38cf7ee6e772c40cd1b6
data/bin/easel CHANGED
@@ -1,3 +1,3 @@
1
- #!/usr/bin/env ruby
1
+ #!/bin/env ruby
2
2
  require 'easel'
3
3
  launch
@@ -1,4 +1,4 @@
1
- #!/snap/bin/ruby
1
+ #!/bin/env ruby
2
2
  #
3
3
  # Author: Eric Power
4
4
 
@@ -8,6 +8,7 @@ require 'erb'
8
8
  # Key Variables
9
9
  @code_names = {
10
10
  200 => "OK",
11
+ 400 => "Bad Request",
11
12
  404 => "Not Found",
12
13
  405 => "Forbidden",
13
14
  418 => "I'm a teapot",
@@ -1,4 +1,4 @@
1
- #!/snap/bin/ruby
1
+ #!/bin/env ruby
2
2
  #
3
3
  # Author: Eric Power
4
4
  #
@@ -68,23 +68,23 @@ $config = {
68
68
  regex: ", (\\d+.\\d+)\\n"
69
69
  }
70
70
  ]
71
- },
72
- {
73
- name: "Memory",
74
- type: "time-series", # uses Time.now.strftime("%H:%M") as x asix.
75
- data: [
76
- {
77
- cmd: "free",
78
- name: "Total Memory",
79
- regex: "Mem:\\W+(\\d+)"
80
- },
81
- {
82
- cmd: "free",
83
- name: "Free Memory",
84
- regex: "Mem:\\W+\\d+\\W+(\\d+)"
85
- }
86
- ]
87
- }
71
+ }#,
72
+ # {
73
+ # name: "Memory",
74
+ # type: "time-series", # uses Time.now.strftime("%H:%M") as x asix.
75
+ # data: [
76
+ # {
77
+ # cmd: "free",
78
+ # name: "Total Memory",
79
+ # regex: "Mem:\\W+(\\d+)"
80
+ # },
81
+ # {
82
+ # cmd: "free",
83
+ # name: "Free Memory",
84
+ # regex: "Mem:\\W+\\d+\\W+(\\d+)"
85
+ # }
86
+ # ]
87
+ # }
88
88
  ]
89
89
  }
90
90
  ]
@@ -1,4 +1,4 @@
1
- #!/snap/bin/ruby
1
+ #!/bin/env ruby
2
2
  #
3
3
  # Author: Eric Power
4
4
 
@@ -1,4 +1,4 @@
1
- #!/snap/bin/ruby
1
+ #!/bin/env ruby
2
2
  #
3
3
  # Author: Eric Power
4
4
  #
data/lib/easel/logging.rb CHANGED
@@ -1,4 +1,4 @@
1
- #!/snap/bin/ruby
1
+ #!/bin/env ruby
2
2
  #
3
3
  # Author: Eric Power
4
4
  #
data/lib/easel/server.rb CHANGED
@@ -1,4 +1,4 @@
1
- #!/snap/bin/ruby
1
+ #!/bin/env ruby
2
2
  #
3
3
  # Author: Eric Power
4
4
  #
@@ -37,6 +37,7 @@ def launch_server
37
37
  log_info "Interrupt received, server shutting down..."
38
38
  rescue Exception => e
39
39
  log_error "Unexpected error occured and closed client connection. Error: #{e}"
40
+ e.backtrace.each { |trace| log_error "#{trace}" }
40
41
  end
41
42
  end
42
43
 
@@ -46,32 +47,46 @@ def handle_request socket
46
47
  log_info "Receieved request: #{socket}"
47
48
  request = read_HTTP_message socket
48
49
 
49
- # TODO: check what the minimum allow handling is. I think there's one more method I need to handle.
50
+ if request.nil?
51
+ socket.print build_error 400
52
+ socket.close
53
+ return
54
+ end
55
+
50
56
  case request[:method]
51
57
  when "GET"
52
- # TODO: respond with app, css file, favicon, or 404 error.
53
58
  if request[:fields][:Upgrade] == "websocket\r\n"
54
59
  run_websocket(socket, request)
55
60
  else
56
61
  handle_get(socket, request)
57
62
  end
58
- #when "HEAD"
59
- # TODO: Deal with HEAD request. https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.4
63
+ when "HEAD"
64
+ if request[:fields][:Upgrade] == "websocket\r\n"
65
+ else
66
+ read_end, write_end = IO.pipe
67
+ handle_get(write_end, request)
68
+ msg = ""
69
+ loop do
70
+ msg += read_end.readline
71
+ if msg.include? "\r\n\r\n"
72
+ socket.print msg
73
+ socket.close
74
+ break
75
+ elsif read_end.readline.nil?
76
+ socket.print build_error 500
77
+ socket.close
78
+ break
79
+ end
80
+ end
81
+ end
60
82
  else
61
- # TODO: respond with an appropriate error.
62
- socket.print "HTTP/1.1 200 OK\r\n" +
63
- "Content-Type: text/plain\r\n" +
64
- "Content-Length: #{response.bytesize}\r\n" +
65
- "Connection: close\r\n" +
66
- "\r\n" +
67
- response
83
+ puts "THIS SHOULD NOT OCCUREEEEEE"
84
+ socket.print build_error 400
68
85
  socket.close
69
86
  end
70
87
 
71
88
  end
72
89
 
73
-
74
-
75
90
  # handle_get
76
91
  #
77
92
  # Handle a get request.
@@ -80,28 +95,28 @@ def handle_get(socket, request)
80
95
  case request[:url]
81
96
  when "/", "/index.html"
82
97
  socket.print build_app
83
- socket.close
84
- when "/test.html" # TODO: Remove this!
85
- socket.print return_html "test.html"
86
- socket.close
98
+ socket.close unless request[:Connection].is_a? String and request[:Connection].downcase == "keep-alive\r\n"
87
99
  when "/app.css"
88
100
  socket.print build_css
89
- socket.close
101
+ socket.close unless request[:Connection].is_a? String and request[:Connection].downcase == "keep-alive\r\n"
90
102
  when "/controller.js"
91
103
  log_info "building controller"
92
104
  socket.print build_js
93
- socket.close
105
+ socket.close unless request[:Connection].is_a? String and request[:Connection].downcase == "keep-alive\r\n"
94
106
  when "/dashboardElements.js"
95
107
  socket.print return_js 'dashboardElements.js'
96
- socket.close
108
+ socket.close unless request[:Connection].is_a? String and request[:Connection].downcase == "keep-alive\r\n"
97
109
  when "/createComponents.js"
98
110
  socket.print return_js 'createComponents.js'
99
- socket.close
111
+ socket.close unless request[:Connection].is_a? String and request[:Connection].downcase == "keep-alive\r\n"
112
+ # TODO: respond with favicon
100
113
  else
101
114
  socket.print build_error 404
102
- socket.close
115
+ socket.close unless request[:Connection].is_a? String and request[:Connection].downcase == "keep-alive\r\n"
103
116
  end
104
117
 
118
+ log_info "Handled HTTP request: #{request}"
119
+
105
120
  end
106
121
 
107
122
 
@@ -110,8 +125,13 @@ end
110
125
  # Read an HTTP message from the socket, and parse it into a request Hash.
111
126
  def read_HTTP_message socket
112
127
  message = []
128
+ first_line = true
113
129
  loop do
114
130
  line = socket.gets
131
+ if first_line
132
+ return nil if line.nil? or not line.match(/^(GET|HEAD|POST|PUT|DELETE|OPTIONS|TRACE) .+ HTTP.+/)
133
+ first_line = false
134
+ end
115
135
  message << line
116
136
  if line == "\r\n"
117
137
  break
@@ -1,4 +1,4 @@
1
- #!/snap/bin/ruby
1
+ #!/bin/env ruby
2
2
  #
3
3
  # Author: Eric Power
4
4
  #
@@ -69,6 +69,8 @@ require_relative 'data_gathering'
69
69
 
70
70
  # Key Variables
71
71
  MAX_WS_FRAME_SIZE = 50.0 # Must be a float number to allow a non-truncated division result.
72
+ # TODO: change to 127.0
73
+
72
74
 
73
75
  # run_websocket
74
76
  #
@@ -76,44 +78,63 @@ MAX_WS_FRAME_SIZE = 50.0 # Must be a float number to allow a non-truncated divi
76
78
  def run_websocket(socket, initial_request)
77
79
 
78
80
  accept_connection(socket, initial_request[:fields][:Sec_WebSocket_Key][0..-3])
81
+ log_info "Accepted WebSocket Connection"
79
82
  child_threads = {}
80
83
  send_msg_mutex = Mutex.new # One mutex per websocket to control sending messages.
81
84
 
85
+
82
86
  Thread.new { # Periodically update the generic dashboard if set.
83
87
  loop do
84
- data = read_data
85
- send_msg(socket, send_msg_mutex, nil, "DASH", data)
88
+ #begin
89
+ data = read_data
90
+ send_msg(socket, send_msg_mutex, nil, "DASH", data)
91
+ #rescue Errno::EPIPE
92
+ # log_info "Pipe closed erorr while sending periodic data to client"
93
+ # break
94
+ #end
86
95
  sleep $config[:collect_data_period]
87
96
  end
88
97
  } unless $config[:collect_data_period] == 0
89
98
 
90
- loop {
91
- msg = receive_msg socket
92
- break if msg.nil? # The socket was closed by the client.
99
+ begin
100
+ loop {
101
+ begin
102
+ msg = receive_msg socket
103
+ rescue Errno::ECONNRESET => e
104
+ log_error "Client reset the connection"
105
+ socket.close
106
+ msg = nil
107
+ end
108
+ break if msg.nil? # The socket was closed by the client.
93
109
 
94
- case msg.split(":")[0]
95
- when "RUN"
96
- cmd_id = msg.match(/^RUN:(.*)$/)[1].to_i
110
+ case msg.split(":")[1]
111
+ when "RUN"
112
+ cmd_id = msg.match(/^RUN:(.*)$/)[1].to_i
97
113
 
98
- unless child_threads[cmd_id]
99
- child_threads[cmd_id] = Thread.new do
100
- run_command_and_stream(socket, cmd_id, send_msg_mutex)
101
- child_threads[cmd_id] = nil
114
+ unless child_threads[cmd_id]
115
+ child_threads[cmd_id] = Thread.new do
116
+ run_command_and_stream(socket, cmd_id, send_msg_mutex)
117
+ child_threads[cmd_id] = nil
118
+ end
102
119
  end
103
- end
104
120
 
105
- when "STOP"
121
+ when "STOP"
106
122
 
107
- cmd_id = msg.match(/^STOP:(.*)$/)[1].to_i
108
- unless child_threads[cmd_id].nil?
109
- child_threads[cmd_id].kill
110
- child_threads[cmd_id] = nil
111
- end
123
+ cmd_id = msg.match(/^STOP:(.*)$/)[1].to_i
124
+ unless child_threads[cmd_id].nil?
125
+ child_threads[cmd_id].kill
126
+ child_threads[cmd_id] = nil
127
+ end
112
128
 
113
- else
114
- log_error "Received an unrecognized message over the websocket: #{msg}"
115
- end
116
- }
129
+ else
130
+ log_error "Received an unrecognized message over the websocket: #{msg}"
131
+ end
132
+ }
133
+ rescue Exception => e
134
+ log_error "Wuh Woh #2: #{e}"
135
+ e.backtrace.each { |trace| log_error "#{trace}" }
136
+ raise e
137
+ end
117
138
 
118
139
  end
119
140
 
@@ -186,12 +207,14 @@ end
186
207
 
187
208
  # receive_msg
188
209
  #
189
- #
210
+ # Extremely naive websocket server. Requires masking, and a message with a length
211
+ # of at most 125. Needs to be updated to conform with RFC 6544
190
212
  def receive_msg socket
191
213
 
192
214
  # Check first two bytes
193
215
  byte1 = socket.getbyte
194
216
  byte2 = socket.getbyte
217
+ return nil if byte1.nil? or byte2.nil?
195
218
  if byte1 == 0x88 # Client is requesting that we close the connection.
196
219
  # TODO: Unsure how to properly handle this case. Right now the socket will close and
197
220
  # everything here will shut down - eventually? Kill all child threads first?
@@ -204,19 +227,28 @@ def receive_msg socket
204
227
  msg_size = byte2 & 0b01111111
205
228
  is_masked = byte2 & 0b10000000
206
229
  unless fin and opcode == 1 and is_masked and msg_size < MAX_WS_FRAME_SIZE
207
- log_error "Invalid websocket message received. #{byte1}-#{byte2}"
208
- puts socket.gets
209
- msg_size.times.map { socket.getbyte } # Read message from socket.
230
+ log_error "Invalid websocket message received. #{fin}, #{opcode == 1}, #{is_masked}, #{msg_size}"
231
+ msg_size.times { socket.getbyte } # Read message from socket.
210
232
  return
211
233
  end
212
234
 
213
235
  # Get message
214
236
  mask = 4.times.map { socket.getbyte }
215
- msg = msg_size.times.map { socket.getbyte }.each_with_index.map {
216
- |byte, i| byte ^ mask[i % 4]
237
+ raise "Not Integer: fin > #{fin }" if not fin.is_a? Integer
238
+ raise "Not Integer: msg_size > #{msg_size }" if not msg_size.is_a? Integer
239
+ raise "Not Integer: opcode > #{opcode }" if not opcode.is_a? Integer
240
+ raise "Not Integer: is_masked > #{is_masked}" if not is_masked.is_a? Integer
241
+ raise "Not aRRAY: mask > #{mask }" if not mask.is_a? Array
242
+ msg = msg_size.times.map { socket.getbyte }.each_with_index.map { |byte, i|
243
+ byte ^ mask[i % 4]
217
244
  }.pack('C*').force_encoding('utf-8').inspect
245
+
246
+ log_info "WebSocket received: #{msg}"
247
+
218
248
  msg[1..-2] # Remove quotation marks from message
219
249
 
250
+
251
+
220
252
  end
221
253
 
222
254
 
@@ -225,7 +257,6 @@ end
225
257
  #
226
258
  def send_msg(socket, send_msg_mutex, cmd_id, msg_type, msg=nil)
227
259
 
228
-
229
260
  case msg_type
230
261
  when "OUT", "ERR" # See comments at the top of the file to explain this part of the protocol.
231
262
  header = "#{cmd_id}:#{msg_type}:"
@@ -295,6 +326,8 @@ def send_msg(socket, send_msg_mutex, cmd_id, msg_type, msg=nil)
295
326
  log_error "Trying to send a websocket message with unrecognized type: #{msg_type}"
296
327
  end
297
328
 
329
+ log_info "Message sent via WebSocket: #{msg}"
330
+
298
331
  end
299
332
 
300
333
  # send_frame
@@ -304,10 +337,11 @@ end
304
337
  # this function).
305
338
  # TODO: Figure out the proper frame size (MAX_WS_FRAME_SIZE).
306
339
  def send_frame(socket, msg)
340
+
307
341
  output = [0b10000001, msg.size, msg]
308
342
  begin
309
343
  socket.write output.pack("CCA#{msg.size}")
310
- rescue IOError
344
+ rescue IOError, Errno::EPIPE
311
345
  log_error "WebSocket is closed. Msg: #{msg}"
312
346
  end
313
347
  end
data/lib/easel.rb CHANGED
@@ -1,4 +1,4 @@
1
- #!/snap/bin/ruby
1
+ #!/usr/bin/env ruby
2
2
  #
3
3
  # Author: Eric Power
4
4
  #
@@ -65,12 +65,8 @@ def parse_ARGV
65
65
  end
66
66
  end
67
67
 
68
- opts.on("-h HOST", "--hostname HOST", "Sets the hostname. Default is '#{$config[:hostname]}'.") do |port|
69
- if port >= 0 and port <= 65535
70
- $config[:port] = port
71
- else
72
- log_fatal "Command argument PORT '#{port}' not a valid port. Must be between 0 and 65535 (inclusive)"
73
- end
68
+ opts.on("-h HOST", "--hostname HOST", "Sets the hostname. Default is '#{$config[:hostname]}'.") do |host|
69
+ $config[:host] = host
74
70
  end
75
71
 
76
72
  opts.on("-o [FILE]", "--output [FILE]", "Set a log file.") do |filename|
@@ -118,9 +118,9 @@ function toggle_run(id){
118
118
  return;
119
119
  }
120
120
  if (tDash.isRunning) { // TODO: Move into the Dashboard Class.
121
- webSocket.send("STOP:" + id);
121
+ webSocket.send(id + ":STOP");
122
122
  } else {
123
- webSocket.send("RUN:" + id);
123
+ webSocket.send(id + ":RUN");
124
124
  }
125
125
  tDash.toggleRunning();
126
126
  }
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: easel-dashboard
3
3
  version: !ruby/object:Gem::Version
4
- version: '0.5'
4
+ version: '0.6'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Eric Power
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-10-01 00:00:00.000000000 Z
11
+ date: 2021-10-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -71,14 +71,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
71
71
  requirements:
72
72
  - - ">="
73
73
  - !ruby/object:Gem::Version
74
- version: '0'
74
+ version: '2.6'
75
75
  required_rubygems_version: !ruby/object:Gem::Requirement
76
76
  requirements:
77
77
  - - ">="
78
78
  - !ruby/object:Gem::Version
79
79
  version: '0'
80
80
  requirements: []
81
- rubygems_version: 3.2.22
81
+ rubygems_version: 3.1.2
82
82
  signing_key:
83
83
  specification_version: 4
84
84
  summary: An easier way to manage your server.