sinatra_sockets 0.0.3 → 0.0.4

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
  SHA1:
3
- metadata.gz: d633c91055c9c1617045a385a61c450dcd65420b
4
- data.tar.gz: c5e8a62363370912095bfab317173cf21c76e2c3
3
+ metadata.gz: c54288802748884e7b74e50b154fab2d598ea346
4
+ data.tar.gz: 363af3452bfd9684bd332d6c7988afc2d2b641b5
5
5
  SHA512:
6
- metadata.gz: 5acd05d9fb2c0371bb4d5b69e753a259e26f0d648e22c9035b9ee0ac0e9fb871d88c73929e3ab5ca37eaf17688ecdccf6bd7363f50c9b10264e39488e65db3e8
7
- data.tar.gz: 6cc81324c4f920f39dcbd2a556eef96f271bb6806e94219132dfba849a633cd0ff7b0fc8fd1113a065545cffb8ff33c19aff15da7b341a068123fa9678e4a511
6
+ metadata.gz: 3e5d8b07d6fe155af2297117a9e253631919e92b5aee628d90facbbcaa26c384a8b829e9fea94fd898026b62551da157feda59a9835a4685a5d82d7d760015c2
7
+ data.tar.gz: 6d59490fd5530989a90e0b031a0deb5069d849c09c4eb3ac819257b83378ba78fef91ff8e7efbb394059341375a572b4697a2769ef33f70074b820e6be59964a
@@ -1,10 +1,9 @@
1
1
  source "http://rubygems.org"
2
2
 
3
- gem 'gemmyrb'
4
3
  gem 'sinatra'
5
4
  gem 'faye-websocket'
6
- gem 'slim'
7
5
  gem "thin"
8
- gem 'rerun'
9
- gem "sass"
10
- gem 'coffee-script'
6
+ gem 'sinatra_auth_github'
7
+ gem 'dotenv'
8
+ gem 'sinatra-cross_origin'
9
+ gem "rack-proxy"
@@ -6,6 +6,8 @@ GEM
6
6
  i18n (~> 0.7)
7
7
  minitest (~> 5.1)
8
8
  tzinfo (~> 1.1)
9
+ addressable (2.5.0)
10
+ public_suffix (~> 2.0, >= 2.0.2)
9
11
  awesome_print (1.7.0)
10
12
  byebug (9.0.6)
11
13
  coderay (1.1.1)
@@ -17,9 +19,12 @@ GEM
17
19
  concurrent-ruby (1.0.4)
18
20
  corefines (1.9.0)
19
21
  daemons (1.2.4)
22
+ dotenv (2.2.0)
20
23
  engtagger (0.2.1)
21
24
  eventmachine (1.2.2)
22
25
  execjs (2.7.0)
26
+ faraday (0.11.0)
27
+ multipart-post (>= 1.2, < 3)
23
28
  faye-websocket (0.10.6)
24
29
  eventmachine (>= 0.12.0)
25
30
  websocket-driver (>= 0.5.1)
@@ -43,15 +48,21 @@ GEM
43
48
  ruby_dep (~> 1.2)
44
49
  method_source (0.8.2)
45
50
  minitest (5.10.1)
51
+ multipart-post (2.0.0)
52
+ octokit (4.6.2)
53
+ sawyer (~> 0.8.0, >= 0.5.3)
46
54
  odyssey (0.2.0)
47
55
  require_all
48
56
  pry (0.10.4)
49
57
  coderay (~> 1.1.0)
50
58
  method_source (~> 0.8.1)
51
59
  slop (~> 3.4)
60
+ public_suffix (2.0.5)
52
61
  rack (1.6.5)
53
62
  rack-protection (1.5.3)
54
63
  rack
64
+ rack-proxy (0.6.0)
65
+ rack
55
66
  rb-fsevent (0.9.8)
56
67
  rb-inotify (0.9.8)
57
68
  ffi (>= 0.5.0)
@@ -60,11 +71,18 @@ GEM
60
71
  listen (~> 3.0)
61
72
  ruby_dep (1.5.0)
62
73
  sass (3.4.23)
74
+ sawyer (0.8.1)
75
+ addressable (>= 2.3.5, < 2.6)
76
+ faraday (~> 0.8, < 1.0)
63
77
  sentence_interpreter (0.0.7)
64
78
  sinatra (1.4.8)
65
79
  rack (~> 1.5)
66
80
  rack-protection (~> 1.4)
67
81
  tilt (>= 1.3, < 3)
82
+ sinatra-cross_origin (0.4.0)
83
+ sinatra_auth_github (1.2.0)
84
+ sinatra (~> 1.0)
85
+ warden-github (~> 1.2.0)
68
86
  slim (3.0.7)
69
87
  temple (~> 0.7.6)
70
88
  tilt (>= 1.3.3, < 2.1)
@@ -79,6 +97,12 @@ GEM
79
97
  tilt (2.0.6)
80
98
  tzinfo (1.2.2)
81
99
  thread_safe (~> 0.1)
100
+ warden (1.2.7)
101
+ rack (>= 1.0)
102
+ warden-github (1.2.0)
103
+ activesupport (> 3.0)
104
+ octokit (> 2.1.0)
105
+ warden (> 1.0)
82
106
  websocket-driver (0.6.5)
83
107
  websocket-extensions (>= 0.1.0)
84
108
  websocket-extensions (0.1.2)
@@ -88,11 +112,15 @@ PLATFORMS
88
112
 
89
113
  DEPENDENCIES
90
114
  coffee-script
115
+ dotenv
91
116
  faye-websocket
92
117
  gemmyrb
118
+ rack-proxy
93
119
  rerun
94
120
  sass
95
121
  sinatra
122
+ sinatra-cross_origin
123
+ sinatra_auth_github
96
124
  slim
97
125
  thin
98
126
 
@@ -1,13 +1,24 @@
1
- A simple sinatra websocket server with faye/eventmaching
2
1
 
3
- to start: `thin start`
2
+ ---
4
3
 
5
- to use command line client, open a new terminal and run `ruby client.rb`.
4
+ This server is adapted from
5
+ [sinatra_sockets](http://github.com/maxpleaner/sinatra_sockets),
6
+ another boiler I made.
6
7
 
7
- To understand what's going on, look at the source code,
8
- and raise an issue on [the github page](http://github.com/maxpleaner/sinatra_sockets)
9
- if there's any confusion.
8
+ ---
10
9
 
11
- **required ruby version** `2.3`
10
+ Steps:
12
11
 
13
- **platform** tested only on MRI and unix
12
+ 1. bundle
13
+ 2. thin start
14
+
15
+ ---
16
+
17
+ This version has a lot of stuff removed since it's serving no front-end.
18
+ It's just the API for the webpack client
19
+
20
+ Important files:
21
+ - websocket stuff in lib/routes/ws.rb
22
+ - regular routes in server.rb
23
+
24
+ ---
@@ -0,0 +1,46 @@
1
+ class Routes::Ws
2
+
3
+ def self.run(request)
4
+ return unless Faye::WebSocket.websocket?(request.env)
5
+ socket = Faye::WebSocket.new(req.env)
6
+ socket.onopen { onopen(request, socket) }
7
+ socket.onmessage { |msg| onmessage(request, ws, msg) }
8
+ socket.onclose { onclose(request, ws) }
9
+ socket.rack_response
10
+ end
11
+
12
+ def self.onopen(request, ws)
13
+ token = request.params["token"]
14
+ unless token
15
+ ws.send({
16
+ msg: "no valid token was sent with websocket; invalid"
17
+ }.to_json)
18
+ ws.close
19
+ return
20
+ end
21
+ Sockets[token] = ws
22
+ end
23
+
24
+ def self.onmessage(request, ws, msg)
25
+ end
26
+
27
+ def self.onclose(request, ws)
28
+ delete_socket(request, ws)
29
+ end
30
+
31
+ class << self
32
+
33
+ private
34
+
35
+ def send_json_message(ws, msg)
36
+ EM.next_tick { ws.send msg.to_json }
37
+ end
38
+
39
+ def delete_socket(request, ws)
40
+ token = CGI.parse(URI.parse(ws.url).to_s)["token"]
41
+ Sockets.delete token
42
+ end
43
+
44
+ end
45
+
46
+ end
@@ -1,80 +1,152 @@
1
- # auto create accessor methods for hash keys
2
- require 'ostruct'
1
+ # ================================================
2
+ # Entry to the Sinatra server
3
+ # ------------------------------------------------
4
+ # This file should not be run directly
5
+ # Run it with "thin start"
6
+ # ================================================
3
7
 
4
- # The "modular" version of Sinatra (no global monkeypatch).
5
8
  require 'sinatra/base'
6
-
7
- # Eventmachine-based websocket server
8
9
  require 'faye/websocket'
9
-
10
- # My gem of Ruby language utils. This in turn requires activesupport,
11
- # colored, awesome_print, etc.
12
- # see http://rubygems.org/gems/gemmyrb
13
- require 'gemmy'
14
-
15
- # A debugger. Pry has more functionality but has become buggy itself.
16
10
  require 'byebug'
17
-
18
- # better than CSS
19
- require 'sass'
20
-
21
- # better than JS
22
- require 'coffee_script'
11
+ require 'sinatra_auth_github'
12
+ require('dotenv'); Dotenv.load
13
+ require 'sinatra/cross_origin'
23
14
 
24
15
  # Requires all ruby files in this directory.
25
16
  # Orders them by the count of "/" in their filename.
26
17
  # Therefore, shallower files are loaded first.
27
- # The reasoning for this is to encourage the common convention of naming
18
+ # The reasoning for this is to support the common convention of naming
28
19
  # files according to their contained class hierarchies.
29
20
  # i.e. class Foo would be in foo.rb,
30
21
  # class Foo::Bar would be in foo/bar.rb,
22
+ #
23
+ # If there is the situation where a class depends on another that is in a
24
+ # deeper-nested file, there's always the option to pass the dependency at
25
+ # runtime.
26
+
31
27
  Dir.glob("./**/*.rb").sort_by { |x| x.count("/") }.each do |path|
32
28
  require path
33
29
  end
34
30
 
35
- # A global array of sockets.
36
- # This can certainly be altered to a hash if faster lookups are desired.
37
- Sockets = []
31
+ # The REST routes (listed in this file) cannot store information in the session
32
+ # since it's on another host.
33
+ # Rather, they pass back and forth a token identifier
34
+ #
35
+ # This is the :token param, and is required on all routes except for
36
+ # /token
37
+ #
38
+ # Here's an outline of the flow:
39
+ #
40
+ # 1. Client hits GET /token, gets a new token
41
+ # 2. Client sends token with websocket connection request at GET /ws
42
+ # 3. Client hits GET /authenticate and goes through Github oAuth login
43
+ # 4. Github sends callback to server, which sends the OK to client over websocket
44
+
45
+ # In leue of sessions, three global objects are used:
46
+ # Users: <Hash> with keys: <username> and vals: <Set(token)>
47
+ # AuthenticatedTokens: <hash> with keys: <token> and vals: <username>
48
+ # Sockets: <hash> with keys: <token> and vals: <socket>
49
+
50
+ Sockets = {}
51
+ AuthenticatedTokens = {}
52
+ Users = Hash.new { |hash, key| hash[key] = Set.new }
38
53
 
39
- # Our server, a Sinatra app
40
54
  class Server < Sinatra::Base
41
55
 
42
- # Thin works well with Faye and Sinatra
43
- # However keep in mind that the server needs to be run with "thin start"
44
- # NOT ruby server.rb,
45
- # rackup,
46
- # rackup -E production
47
- # etc.
48
- # These will not work.
49
56
  set :server, 'thin'
50
57
  Faye::WebSocket.load_adapter('thin')
51
58
 
52
- # The root route, which handles both websocket and HTTP requests
53
- # See server_skeleton/lib/routes/index.rb
54
- get '/' do
55
- Routes::Index.run(request_obj)
59
+ # Allow some routes to be accessed from different origins
60
+ # This is unnecessary for websocket requests, since browsers don't implement
61
+ # the same restictions.
62
+
63
+ register Sinatra::CrossOrigin
64
+
65
+ # Github oAuth setup
66
+ # session is needed for the Github oAuth gem
67
+ # but its not used elsewhere
68
+
69
+ enable :sessions
70
+ set :github_options, {
71
+ scopes: "user",
72
+ secret: ENV["GITHUB_CLIENT_SECRET"],
73
+ client_id: ENV["GITHUB_CLIENT_ID"]
74
+ }
75
+ register Sinatra::Auth::Github
76
+
77
+ # ------------------------------------------------
78
+ # Standard HTTP routes
79
+ # (get '/ws' is the entrance to the websocket API)
80
+ # ------------------------------------------------
81
+
82
+ # First clients request a token
83
+
84
+ get '/token' do
85
+ cross_origin allow_origin: "http://localhost:8080"
86
+ { token: new_token }.to_json
56
87
  end
57
88
 
58
- # The 'request' variable is passed along to route handlers
59
- # Some extra information is attached to make for a simpler API
60
- def request_obj
61
- if !defined?(request.renderers)
62
- _renderers = method(:renderers)
63
- request.define_singleton_method(:renderers) { _renderers.call }
89
+ # Then they send it in websocket connection request
90
+ # See server/lib/routes/ws.rb
91
+
92
+ get '/ws' do
93
+ Routes::Ws.run(request)
94
+ end
95
+
96
+ # Then they authenticate with Github
97
+ # TODO render a proper HTML page after this not just plaintext
98
+ # saying they can close the window.
99
+
100
+ get '/authenticate' do
101
+ if token = params["token"]
102
+ if socket = Sockets[token]
103
+ if !AuthenticatedTokens[token]
104
+ authenticate!
105
+ username = get_username
106
+ Users[username] << (token)
107
+ AuthenticatedTokens[token] = username
108
+ end
109
+ socket.send({
110
+ action: "logged_in",
111
+ username: username
112
+ }.to_json)
113
+ "authenticated as #{username}. (this window can be closed)"
114
+ else
115
+ "error. lost your websocket connection (this window can be closed)"
116
+ end
117
+ else
118
+ "error. That request requires a token (this window can be closed)"
64
119
  end
65
- request
66
120
  end
67
121
 
68
- # For now, the only extra information being sent in 'request'
69
- # is the method 'slim', which would otherwise be unavailable in
70
- # the scope of a different class.
71
- def renderers
72
- OpenStruct.new(
73
- slim: method(:slim)
74
- )
122
+ get '/logout' do
123
+ token = params[:token]
124
+ if token
125
+ if username = AuthenticatedTokens[token]
126
+ logout!
127
+ Sockets[token].send({
128
+ action: "logged_out"
129
+ }.to_json)
130
+ else
131
+ if ws = Sockets[token]
132
+ ws.send({msg: "can't find user to log out"}.to_json)
133
+ else
134
+ {error: "can't find user to log out"}.to_json
135
+ end
136
+ end
137
+ else
138
+ { error: 'cant log out; no token provided' }.to_json
139
+ end
75
140
  end
76
141
 
77
- end
142
+ private
78
143
 
79
- # This file should not be run directly
80
- # Run it with "thin start"
144
+ def get_username
145
+ github_user.login
146
+ end
147
+
148
+ def new_token
149
+ SecureRandom.urlsafe_base64
150
+ end
151
+
152
+ end
data/lib/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module SinatraSockets
2
- VERSION = '0.0.3'
2
+ VERSION = '0.0.4'
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sinatra_sockets
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - maxpleaner
@@ -36,22 +36,10 @@ files:
36
36
  - lib/server_skeleton/Gemfile
37
37
  - lib/server_skeleton/Gemfile.lock
38
38
  - lib/server_skeleton/README.md
39
- - lib/server_skeleton/client.rb
40
39
  - lib/server_skeleton/config.ru
41
- - lib/server_skeleton/deps.js
42
40
  - lib/server_skeleton/lib/routes.rb
43
- - lib/server_skeleton/lib/routes/index.rb
44
- - lib/server_skeleton/lib/websocket.rb
45
- - lib/server_skeleton/package.json
46
- - lib/server_skeleton/public/bundle.js
41
+ - lib/server_skeleton/lib/routes/ws.rb
47
42
  - lib/server_skeleton/server.rb
48
- - lib/server_skeleton/views/index.slim
49
- - lib/server_skeleton/views/partials/url_input.slim
50
- - lib/server_skeleton/views/partials/videos.slim
51
- - lib/server_skeleton/views/script/add_video.slim
52
- - lib/server_skeleton/views/script/url_input.slim
53
- - lib/server_skeleton/views/script/websocket.slim
54
- - lib/server_skeleton/views/style/main.slim
55
43
  - lib/sinatra_sockets.rb
56
44
  - lib/version.rb
57
45
  homepage: http://github.com/maxpleaner/sinatra_sockets
@@ -1,33 +0,0 @@
1
- require 'faye/websocket'
2
- require 'eventmachine'
3
- require 'byebug'
4
- require 'gemmy'
5
-
6
- if $start_client
7
-
8
- Settings = {awaiting_input: false }
9
- EM.run do
10
-
11
- EM.tick_loop do
12
- if !Settings[:awaiting_input]
13
- Thread.new do
14
- Settings[:awaiting_input] = true
15
- inp = gets.chomp
16
- Settings[:awaiting_input] = false
17
- Ws.send inp
18
- end
19
- sleep 0.2
20
- end
21
- end.on_stop { EM.stop }
22
-
23
- ServerWebsocketUrl = ENV["SERVER_WS_URL"] || 'ws://localhost:3000/'
24
-
25
- Ws = Faye::WebSocket::Client.new ServerWebsocketUrl
26
-
27
- Ws.on :message do |event|
28
- puts event.data
29
- end
30
-
31
- end
32
-
33
- end
@@ -1,3 +0,0 @@
1
- window.valid_url = require("valid-url");
2
- window.jquery = require("jquery");
3
- window.$ = jquery
@@ -1,48 +0,0 @@
1
- class Routes::Index
2
-
3
- Gemmy.patches.each { |patch| using patch }
4
- # This method is called from the Sinatra route handler
5
- def self.run(request)
6
- is_websocket = websocket_request? request
7
- m(is_websocket ? :websocket_request : :http_request).call request
8
- end
9
-
10
- # Check if the request is of the websocket variety
11
- def self.websocket_request?(request)
12
- Faye::WebSocket.websocket? request.env
13
- end
14
-
15
- # Handle HTTP requests
16
- def self.http_request request
17
- request.renderers.slim.call :index
18
- end
19
-
20
- # Handle websocket requests
21
- def self.websocket_request request
22
- socket = Websocket.new request
23
- socket.onopen &method(:onopen)
24
- socket.onmessage &method(:onmessage)
25
- socket.onclose &method(:onclose)
26
- socket.ready
27
- end
28
-
29
- # Handler for newly opened websocket
30
- # Pushes the websocket connection object into the global Sockets array
31
- def self.onopen(request, ws)
32
- Sockets << ws
33
- end
34
-
35
- # Handler for received message from websocket
36
- # Sends a message to all clients echoing what was received
37
- def self.onmessage(request, ws, msg)
38
- EM.next_tick { Sockets.each{|s| s.send "got #{msg.data}" } }
39
- end
40
-
41
- # Handler for closed websocket event
42
- # Deletes the websocket connection object from the global Sockets array
43
- def self.onclose(request, ws)
44
- warn("websocket closed")
45
- Sockets.delete(ws)
46
- end
47
-
48
- end
@@ -1,48 +0,0 @@
1
- class Websocket
2
-
3
- # A wrapper over an underlying websocket API
4
- # If ever the websocket server dependency would need to change,
5
- # this file would be a place to swap out libraries.
6
- #
7
- # With each handler (open, close, message) is passed the request object
8
- # in addition to the websocket connection object and any additional arguments
9
- # (such as message)
10
- #
11
- # If this is being initialized, the request is assumed to be of the
12
- # websocket variety.
13
- #
14
- # Operations in this class are implemented in a stack which needs to be
15
- # finalized by #ready.
16
- #
17
- # for example:
18
- # socket = Websocket.new(request)
19
- # socket.onopen { |req, socket| }
20
- # socket.onclose { |req, socket| }
21
- # socket.onmessage { |req, socket, msg| }
22
- # socket.ready
23
- #
24
- def initialize(req)
25
- @req = req
26
- @socket = Faye::WebSocket.new(req.env)
27
- @stack = {}
28
- end
29
-
30
- def onopen &blk
31
- @stack[:onopen] = blk
32
- end
33
-
34
- def onclose &blk
35
- @stack[:onclose] = blk
36
- end
37
-
38
- def onmessage &blk
39
- @stack[:onmessage] = blk
40
- end
41
-
42
- def ready
43
- @socket.on(:open) { @stack[:onopen].call @req, @socket }
44
- @socket.on(:close) { @stack[:onclose].call @req, @socket }
45
- @socket.on(:message) { |msg| @stack[:onmessage].call @req, @socket, msg }
46
- @socket.rack_response
47
- end
48
- end
@@ -1,17 +0,0 @@
1
- {
2
- "name": "mixer",
3
- "version": "1.0.0",
4
- "description": "A simple sinatra websocket server with faye/eventmaching",
5
- "main": "index.js",
6
- "scripts": {
7
- "test": "echo \"Error: no test specified\" && exit 1"
8
- },
9
- "author": "",
10
- "license": "ISC",
11
- "dependencies": {
12
- "bower": "^1.8.0",
13
- "browserify": "^14.1.0",
14
- "jquery": "^3.1.1",
15
- "valid-url": "^1.0.9"
16
- }
17
- }