easel-dashboard 0.5 → 0.6

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: 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.