plezi 0.7.7 → 0.8.1

Sign up to get free protection for your applications and to get access to all the features.
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)"