plezi 0.7.7 → 0.8.1

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.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +26 -0
  3. data/README.md +3 -3
  4. data/lib/plezi.rb +35 -36
  5. data/lib/plezi/common/cache.rb +94 -0
  6. data/lib/plezi/{base → common}/dsl.rb +87 -68
  7. data/lib/plezi/{base → common}/logging.rb +15 -15
  8. data/lib/plezi/eventmachine/connection.rb +192 -0
  9. data/lib/plezi/eventmachine/em.rb +95 -0
  10. data/lib/plezi/eventmachine/io.rb +265 -0
  11. data/lib/plezi/eventmachine/protocol.rb +54 -0
  12. data/lib/plezi/eventmachine/queue.rb +51 -0
  13. data/lib/plezi/eventmachine/ssl_connection.rb +138 -0
  14. data/lib/plezi/eventmachine/timers.rb +117 -0
  15. data/lib/plezi/eventmachine/workers.rb +35 -0
  16. data/lib/plezi/handlers/http_host.rb +4 -4
  17. data/lib/plezi/handlers/magic_helpers.rb +1 -21
  18. data/lib/plezi/handlers/route.rb +3 -3
  19. data/lib/plezi/server/{helpers/http.rb → http.rb} +1 -57
  20. data/lib/plezi/server/{protocols/http_protocol.rb → http_protocol.rb} +18 -35
  21. data/lib/plezi/server/{protocols/http_request.rb → http_request.rb} +1 -1
  22. data/lib/plezi/server/{protocols/http_response.rb → http_response.rb} +45 -22
  23. data/lib/plezi/server/{helpers/mime_types.rb → mime_types.rb} +0 -0
  24. data/lib/plezi/server/{protocols/websocket.rb → websocket.rb} +34 -37
  25. data/lib/plezi/server/{protocols/ws_response.rb → ws_response.rb} +37 -19
  26. data/lib/plezi/version.rb +1 -1
  27. data/plezi.gemspec +3 -3
  28. data/resources/config.ru +9 -11
  29. metadata +27 -30
  30. data/lib/plezi/base/cache.rb +0 -89
  31. data/lib/plezi/base/connections.rb +0 -33
  32. data/lib/plezi/base/engine.rb +0 -77
  33. data/lib/plezi/base/events.rb +0 -93
  34. data/lib/plezi/base/io_reactor.rb +0 -62
  35. data/lib/plezi/base/rack_app.rb +0 -89
  36. data/lib/plezi/base/services.rb +0 -57
  37. data/lib/plezi/base/timers.rb +0 -71
  38. data/lib/plezi/server/README.md +0 -33
  39. data/lib/plezi/server/services/basic_service.rb +0 -224
  40. data/lib/plezi/server/services/no_service.rb +0 -196
  41. data/lib/plezi/server/services/ssl_service.rb +0 -193
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 549ca7646d6226a6beb69854ecd8076fba8fbd62
4
- data.tar.gz: 52fa9fe134cbcbb7bdd018e1c0d36d9cd0bcc7f9
3
+ metadata.gz: 1850b2cae3cd76cdda78e0c52b8437c923625085
4
+ data.tar.gz: 671b9720bea6f662c77cc7806ecd86cd5bb0c7c7
5
5
  SHA512:
6
- metadata.gz: 5833877cd84d581d347d97ee28bc7fb8b82c8f702d11f5f0989a2aa12c2a21507103c147af928ad929020683d3e6219661a09267a413e36ce3b9e9b8976f328f
7
- data.tar.gz: d322ad5cfd6de248e5f018b0de2fa98289b8f0753aaa10b2cee9599c95fd30b0edbeebb235c7d573f3c03eed7766cb2cb4d455c78a0dfc8101c64dbbca16cf0b
6
+ metadata.gz: 0f9083419bd7c5dcd3f10dfa1004e247b8960e711a85568546a5cfd59643bca952b042f1a3db9e9c9cac763a90da8e84e69f101e265fc5809a5dc265d8e21552
7
+ data.tar.gz: aea670217030a98ad2e3329ceb65ef59b18356535d71c9c75bd510ec098e26dc95079e627ea04aaee593da0c71293caa1e8405760d36ed1b65afafc6edd6e48e
@@ -2,6 +2,32 @@
2
2
 
3
3
  ***
4
4
 
5
+ Change log v.0.8.1
6
+
7
+ **fix**: fixed an issue that silently prevented SSL connections from working properly. SSL was mistakenly disabled and normal connections were attempted. This issue should have cause a no-service situation, as attempting to connect using SSL to a non-SSL connection would fail.
8
+
9
+ **fix**: fixed Websocket connections. An extra EOL marker at the end of the HTTP upgrade responce caused websockets to fail. The excess new line marker was removed.
10
+
11
+ ***
12
+
13
+ Change log v.0.8.0
14
+
15
+ **Refactoring**: core code was refractored. Older code __might__ not work.
16
+
17
+ Most apps should be able to upgrade with no issues.
18
+
19
+ **Rack support might be broken** (again)... Rack support has changed and it's probably boken. It might come back, and it might not - it was originally only for testing. Plezi is a pure Ruby Rack alternative. It runs it's own web server (since, unlike Rack, the response objects are part of the server and this allows for a whole lot of magic and concurrency to happen). I hadn't had the time to test the Rack support under the new code.
20
+
21
+ **Events API changes**: the method Plezi.push_event has been removed. Plezi.run_async and Plezi.callback are the preffered methods to be used for event handling.
22
+
23
+ **Timers API changes**: The API for timed events has changed. It is now both more streamlined and allows setting a repeat limit for events that repeat themselves (i.e. schedule an event to repeat every 60 seconds and limit it to perform only 5 times).
24
+
25
+ **fix**: Fixed an issue with attachments. Attachments were mistakenly enforced to comply with UTF-8 encoding, preventing binary attachments from successfully uploading.
26
+
27
+ unstable API warning - **Effects only advanced users**: v.0.8.0 is a time for change. The regular API (the DSL and some Plezi helpers) will remain stable (and if it breaks, we will fix it), but the core API - which some users might have used even though they probably shouldn't have - will change.
28
+
29
+ ***
30
+
5
31
  Change log v.0.7.7
6
32
 
7
33
  **fix**: fixed a header issue in the HTTPResponse that prevented websocket connections.
data/README.md CHANGED
@@ -153,7 +153,7 @@ More on asynchronous events and timers later.
153
153
 
154
154
  Plezi supports magic routes, in similar formats found in other systems, such as: `route "/:required/(:optional_with_format){[\\d]*}/(:optional)", Plezi::StubRESTCtrl`.
155
155
 
156
- Plezi assummes all simple string routes to be RESTful routes woth the parameter `:id` ( `"/user" == "/user/(:id)"` ).
156
+ Plezi assummes all simple string routes to be RESTful routes with the parameter `:id` ( `"/user" == "/user/(:id)"` ).
157
157
 
158
158
  require 'plezi'
159
159
  listen
@@ -232,7 +232,7 @@ Asynchronous callbacks (works only while services are active and running):
232
232
  require 'plezi'
233
233
 
234
234
  def my_shutdown_proc time_start
235
- puts "Services were running for #{Time.now - time_start} ms."
235
+ puts "Services were running for #{Time.now - time_start} seconds."
236
236
  end
237
237
 
238
238
  # shutdown callbacks
@@ -321,7 +321,7 @@ Feel free to fork or contribute. right now I am one person, but together we can
321
321
 
322
322
  ## Contributing
323
323
 
324
- 1. Fork it ( https://github.com/boazsegev/plezi-server/fork )
324
+ 1. Fork it ( https://github.com/boazsegev/plezi/fork )
325
325
  2. Create your feature branch (`git checkout -b my-new-feature`)
326
326
  3. Commit your changes (`git commit -am 'Add some feature'`)
327
327
  4. Push to the branch (`git push origin my-new-feature`)
@@ -13,28 +13,51 @@ require 'time'
13
13
  require 'json'
14
14
  require 'uri'
15
15
  require 'set'
16
+
17
+ ## erb templating
18
+ begin
19
+ require 'erb'
20
+ rescue Exception => e
21
+
22
+ end
23
+
16
24
  ### version
17
25
 
18
26
  require "plezi/version"
19
27
 
28
+ ### common helpers
29
+
30
+ require 'plezi/common/logging.rb'
31
+ require 'plezi/common/cache.rb'
32
+ require 'plezi/common/dsl.rb'
33
+
34
+ ### event machine
35
+
36
+ require "plezi/eventmachine/em.rb"
37
+
38
+ require 'plezi/eventmachine/queue.rb'
39
+ require 'plezi/eventmachine/workers.rb'
40
+ require 'plezi/eventmachine/timers.rb'
41
+ require 'plezi/eventmachine/io.rb'
42
+ require 'plezi/eventmachine/protocol.rb'
43
+ require 'plezi/eventmachine/connection.rb'
44
+ require 'plezi/eventmachine/ssl_connection.rb'
20
45
 
21
- ### Server requirements
46
+ ### http and websocket server
22
47
 
23
- require "plezi/server/services/basic_service"
24
- require "plezi/server/services/ssl_service"
25
- require "plezi/server/services/no_service"
26
48
 
27
- require "plezi/server/protocols/http_protocol"
28
- require 'plezi/server/protocols/http_request'
29
- require 'plezi/server/protocols/http_response'
49
+ require 'plezi/server/mime_types.rb'
30
50
 
31
- require "plezi/server/helpers/http"
32
- require "plezi/server/helpers/mime_types"
51
+ require 'plezi/server/http.rb'
52
+ require 'plezi/server/http_protocol.rb'
53
+ require 'plezi/server/http_request.rb'
54
+ require 'plezi/server/http_response.rb'
33
55
 
34
- require "plezi/server/protocols/websocket"
35
- require 'plezi/server/protocols/ws_response'
56
+ require 'plezi/server/websocket.rb'
57
+ require 'plezi/server/ws_response.rb'
58
+
59
+ ### Handlers / Framework
36
60
 
37
- ## Server-Framework Bridges
38
61
  require "plezi/handlers/http_echo"
39
62
  require "plezi/handlers/http_host"
40
63
  require "plezi/handlers/http_router"
@@ -45,30 +68,6 @@ require "plezi/handlers/route"
45
68
 
46
69
  require "plezi/handlers/stubs"
47
70
 
48
- ### Framework requirements
49
- require "plezi/base/events"
50
- require "plezi/base/timers"
51
- require "plezi/base/services"
52
- require "plezi/base/connections"
53
- require "plezi/base/logging"
54
- require "plezi/base/io_reactor"
55
- require "plezi/base/cache"
56
- require "plezi/base/engine"
57
-
58
- ### DSL requirements
59
- require "plezi/base/dsl"
60
-
61
- ### optional Rack
62
- require "plezi/base/rack_app"
63
-
64
- ## erb templating
65
- begin
66
- require 'erb'
67
- rescue Exception => e
68
-
69
- end
70
-
71
-
72
71
 
73
72
  ##############################################################################
74
73
  #
@@ -0,0 +1,94 @@
1
+
2
+ module Plezi
3
+
4
+ # File and Object Caching for Plezi
5
+ module Cache
6
+ # contains the cached data, in the format: CACHE_STORE["filename"] = CacheObject
7
+ CACHE_STORE = {}
8
+ CACHE_LOCK = Mutex.new
9
+ CACHABLE = %w{cache object slim haml css map js html scss sass coffee txt xml json yaml rb}
10
+
11
+ @cache_to_disk = true
12
+
13
+ # this class holds cached objects (data and modification times)
14
+ class CacheObject
15
+ # Cached attributes
16
+ attr_accessor :data, :mtime
17
+
18
+ # initialize a Cached object
19
+ def initialize d = nil, t = Time.now
20
+ @data, @mtime = d, t
21
+ end
22
+ end
23
+
24
+ # load the file from the cache (if exists) or the file system (if it doesn't)
25
+ def load_file filename
26
+ cached?(filename) ? get_cached(filename) : reload_file(filename)
27
+ end
28
+ # review a file's modification time
29
+ def file_mtime filename
30
+ return CACHE_STORE[filename].mtime if cached?(filename)
31
+ File.mtime(filename)
32
+ end
33
+
34
+ # force a file onto the cache (only if it is cachable - otherwise will load the file but will not cache it).
35
+ def reload_file filename
36
+ if CACHABLE.include? filename.match(/\.([^\.]+)$/)[1]
37
+ return cache_data filename, IO.read(filename), File.mtime(filename)
38
+ else
39
+ return IO.read(filename)
40
+ end
41
+ end
42
+ # places data into the cache, and attempts to save the data to a file name.
43
+ def save_file filename, data, save_to_disk = false
44
+ cache_data filename, data if CACHABLE.include? filename.match(/\.([^\.]+)$/)[1]
45
+ begin
46
+ IO.write filename, data if save_to_disk
47
+ rescue Exception => e
48
+ Plezi.warn("File couldn't be written (#{filename}) - file system error?")
49
+ end
50
+ data
51
+ end
52
+
53
+ # places data into the cache, under an identifier ( file name ).
54
+ def cache_data filename, data, mtime = Time.now
55
+ CACHE_LOCK.synchronize { CACHE_STORE[filename] = CacheObject.new( data, mtime ) }
56
+ data
57
+ end
58
+
59
+ # Get data from the cache. will throw an exception if there is no data in the cache.
60
+ def get_cached filename
61
+ CACHE_STORE[filename].data # if CACHE_STORE[filename]
62
+ end
63
+
64
+ # Remove data from the cache, if it exists.
65
+ def clear_cached filename
66
+ CACHE_LOCK.synchronize { CACHE_STORE.delete filename } # if CACHE_STORE[filename]
67
+ end
68
+
69
+ # clears all cached data.
70
+ def clear_cache! filename
71
+ CACHE_LOCK.synchronize { CACHE_STORE.clear } # if CACHE_STORE[filename]
72
+ end
73
+
74
+ # returns true if the filename is cached.
75
+ def cached? filename
76
+ !CACHE_STORE[filename].nil?
77
+ end
78
+
79
+ # returns true if the file exists on disk or in the cache.
80
+ def file_exists? filename
81
+ (CACHE_STORE[filename] || File.exists?(filename)) ? true : false
82
+ end
83
+
84
+ # returns true if the file has been update since data was last cached.
85
+ def cache_needs_update? filename
86
+ return true if CACHE_STORE[filename].nil? || CACHE_STORE[filename].mtime < File.mtime(filename)
87
+ false
88
+ end
89
+ end
90
+
91
+ public
92
+
93
+ extend Plezi::Cache
94
+ end
@@ -1,55 +1,19 @@
1
1
 
2
2
  module Plezi
3
3
 
4
- # this module contains the methods that are used as a DSL and sets up easy access to the Plezi framework.
5
- #
6
- # use the`listen`, `host` and `route` functions rather then accessing this object.
4
+ # holds methods that are called by the DSL.
7
5
  #
6
+ # this isn't part of the public API.
8
7
  module DSL
9
8
  module_function
10
9
 
10
+ # this module contains the methods that are used as a DSL and sets up easy access to the Plezi framework.
11
+ #
12
+ # use the`listen`, `host` and `route` functions rather then accessing this object.
13
+ #
11
14
  @servers = {}
12
15
  @active_router = nil
13
16
 
14
- # adds a server (performs the action required by the listen method).
15
- #
16
- # accepts:
17
- # port:: (optional) the port number for the service. if not defined, defaultes to the -p runtime argument (i.e. `./app.rb -p 8080`) or to 3000 if argument is missing.
18
- # params:: any parameter accepted by the Plezi.add_service method. defaults to: `:protocol=>Plezi::HTTPProtocol, :handler => HTTPRouter.new`
19
- def listen(port, params = {})
20
- # set port and arguments
21
- if port.is_a?(Hash)
22
- params = port
23
- port = nil
24
- end
25
- if !port && defined? ARGV
26
- if ARGV.find_index('-p')
27
- port_index = ARGV.find_index('-p') + 1
28
- port ||= ARGV[port_index].to_i
29
- ARGV[port_index] = (port + 1).to_s
30
- else
31
- ARGV << '-p'
32
- ARGV << '3000'
33
- return listen nil, params
34
- end
35
- end
36
- port ||= 3000
37
-
38
- # create new service or choose existing
39
- if @servers[port]
40
- puts "WARNING: port aleady in use! returning existing service and attemptin to add host (maybe multiple hosts? use `host` instead)." unless params[:host]
41
- @active_router = @servers[port][:handler]
42
- @active_router.add_host params[:host], params
43
- return @active_router
44
- end
45
- params[:protocol] ||= HTTPProtocol
46
- @active_router = params[:handler] ||= HTTPRouter.new # HTTPEcho #
47
- @active_router.add_host params[:host], params
48
- return false unless Plezi.add_service(port, params)
49
- @servers[port] = params
50
- @active_router
51
- end
52
-
53
17
  # adds a route to the last server created
54
18
  def route(path, controller = nil, &block)
55
19
  @active_router.add_route path, controller, &block
@@ -58,7 +22,7 @@ module Plezi
58
22
 
59
23
  # adds a shared route to all existing services and hosts.
60
24
  def shared_route(path, controller = nil, &block)
61
- @servers.values.each {|p| p[:handler].add_shared_route path, controller, &block }
25
+ @listeners.values.each {|p| (defined?(PLEZI_ON_RACK) ? p[:handler] : p.params[:handler]).add_shared_route path, controller, &block }
62
26
  end
63
27
 
64
28
  # adds a host to the last server created
@@ -68,36 +32,91 @@ module Plezi
68
32
  def host(host_name, params)
69
33
  @active_router.add_host host_name, params
70
34
  end
35
+
36
+ # Rack application model support
37
+
38
+ # Plezi dresses up for Rack - this is a watered down version missing some features (such as flash and WebSockets).
39
+ # a full featured Plezi app, with WebSockets, requires the use of the Plezi server
40
+ # (the built-in server)
41
+ def call env
42
+ raise "No Plezi Services" unless @listeners && @listeners.any?
43
+
44
+ # re-encode to utf-8, as it's all BINARY encoding at first
45
+ env['rack.input'].rewind
46
+ env['rack.input'] = StringIO.new env['rack.input'].read.encode('utf-8', 'binary', invalid: :replace, undef: :replace, replace: '')
47
+ env.each do |k, v|
48
+ if k.to_s.match /^[A-Z]/
49
+ if v.is_a?(String) && !v.frozen?
50
+ v.force_encoding('binary').encode!('utf-8', 'binary', invalid: :replace, undef: :replace, replace: '') unless v.force_encoding('utf-8').valid_encoding?
51
+ end
52
+ end
53
+ end
54
+ # re-key params
55
+ # new_params = {}
56
+ # env[:params].each {|k,v| HTTP.add_param_to_hash k, v, new_params}
57
+ # env[:params] = new_params
58
+
59
+ # make hashes magical
60
+ make_hash_accept_symbols(env)
61
+
62
+ # use Plezi Cookies
63
+ env['rack.request.cookie_string'] = env['HTTP_COOKIE']
64
+ env['rack.request.cookie_hash'] = Plezi::Cookies.new.update(env['rack.request.cookie_hash'] || {})
65
+
66
+ # chomp path
67
+ env['PATH_INFO'].chomp! '/'
68
+
69
+ # get response
70
+ response = @listeners.first[1][:handler].call env
71
+
72
+ return response if response.is_a?(Array)
73
+
74
+ return [404, {}, ['not found']] if response === true
75
+
76
+ response.prep_rack
77
+ headers = response.headers
78
+ # headers.delete 'transfer-encoding'
79
+ # headers.delete 'connection'
80
+ # set cookie headers
81
+ unless response.cookies.empty?
82
+ headers['Set-Cookie'] = []
83
+ response.cookies.each {|k,v| headers['Set-Cookie'] << ("#{k.to_s}=#{v.to_s}")}
84
+ end
85
+ [response.status, headers, response.body]
86
+ end
87
+
88
+ # tweeks a hash object to read both :symbols and strings (similar to Rails but without).
89
+ def make_hash_accept_symbols hash
90
+ @magic_hash_proc ||= Proc.new do |hs,k|
91
+ if k.is_a?(Symbol) && hs.has_key?( k.to_s)
92
+ hs[k.to_s]
93
+ elsif k.is_a?(String) && hs.has_key?( k.to_sym)
94
+ hs[k.to_sym]
95
+ elsif k.is_a?(Numeric) && hs.has_key?(k.to_s.to_sym)
96
+ hs[k.to_s.to_sym]
97
+ end
98
+ end
99
+ hash.default_proc = @magic_hash_proc
100
+ hash.values.each do |v|
101
+ if v.is_a?(Hash)
102
+ make_hash_accept_symbols v
103
+ end
104
+ end
105
+ end
106
+
71
107
  end
72
108
  end
73
109
 
74
110
  Encoding.default_internal = 'utf-8'
75
111
  Encoding.default_external = 'utf-8'
76
112
 
77
- # Set a shortcut for the Plezi module.
113
+ # PL is a shortcut for the Plezi module, so that `PL == Plezi`.
78
114
  PL = Plezi
79
115
 
80
- # creates a server object and waits for routes to be set.
81
- #
82
- # port:: the port to listen to. the first port defaults to 3000 and increments by 1 with every `listen` call. it's possible to set the first port number by running the app with the -p paramater.
83
- # params:: a Hash of serever parameters, as listed in the Plezi#add_service documentation.
84
- #
85
- # The different keys in the params hash control the server's behaviour, as follows:
86
- #
87
- # host:: the host name. defaults to any host not explicitly defined (a catch-all).
88
- # alias:: a String or an Array of Strings which represent alternative host names (i.e. `alias: ["admin.google.com", "admin.gmail.com"]`).
89
- # root:: the public root folder. if this is defined, static files will be served from the location.
90
- # assets:: the assets root folder. defaults to nil (no assets support). if the path is defined, assets will be served from `/assets/...` (or the public_asset path defined) before any static files. assets will not be served if the file in the /public/assets folder if up to date (a rendering attempt will be made for systems that allow file writing).
91
- # assets_public:: the assets public uri location (uri format, NOT a file path). defaults to `/assets`. assets will be saved (or rendered) to the assets public folder and served as static files.
92
- # assets_callback:: a method that accepts one parameters: `request` and renders any custom assets. the method should return `false` unless it has created a response object (`response = Plezi::HTTPResponse.new(request)`) and sent a response to the client using `response.finish`.
93
- # save_assets:: saves the rendered assets to the filesystem, under the public folder. defaults to false.
94
- # templates:: the templates root folder. defaults to nil (no template support). templates can be rendered by a Controller class, using the `render` method.
95
- # ssl:: if true, an SSL service will be attempted. if no certificate is defined, an attempt will be made to create a self signed certificate.
96
- # ssl_key:: the public key for the SSL service.
97
- # ssl_cert:: the certificate for the SSL service.
98
- #
99
- def listen(port = nil, params = {})
100
- Plezi::DSL.listen port, params
116
+ # shortcut for Plezi.listen.
117
+ #
118
+ def listen(params = {})
119
+ Plezi::DSL.listen params
101
120
  end
102
121
 
103
122
  # adds a virtul host to the current service (the last `listen` call) or switches to an existing host within the active service.
@@ -180,7 +199,7 @@ def start_services
180
199
  undef route
181
200
  undef shared_route
182
201
  undef start_services
183
- Plezi.start_services
202
+ Plezi::DSL.start_services
184
203
  end
185
204
 
186
205
  # restarts the Plezi app with the same arguments as when it was started.
@@ -193,7 +212,7 @@ end
193
212
  # sets to start the services once dsl script is finished loading.
194
213
  at_exit { start_services } unless ( defined?(NO_PLEZI_AUTO_START) || defined?(BUILDING_PLEZI_TEMPLATE) || defined?(PLEZI_ON_RACK) )
195
214
 
196
- # sets a name for the process (on some systems).
215
+ # sets information to be used when restarting
197
216
  $PL_SCRIPT = $0
198
217
  $PL_ARGV = $*.dup
199
- $0="Plezi (Ruby)"
218
+ # $0="Plezi (Ruby)"