sinatra_sockets 0.0.3 → 0.0.4

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
  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
- }