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
@@ -0,0 +1,54 @@
1
+ module Plezi
2
+
3
+
4
+ module EventMachine
5
+
6
+ # this module is the protocol (controller) for the HTTP server.
7
+ #
8
+ #
9
+ # to do: implemet logging, support body types: multipart (non-ASCII form data / uploaded files), json & xml
10
+ class Protocol
11
+
12
+ attr_accessor :connection, :params
13
+ attr_reader :buffer, :locker
14
+
15
+ # initializes the protocol object
16
+ def initialize connection, params
17
+ @buffer, @connection, @params, @locker = [], connection, params, connection.locker
18
+ end
19
+
20
+ # called when connection is initialized.
21
+ def on_connect
22
+ end
23
+
24
+ # called after data is recieved.
25
+ #
26
+ # this method is called within a lock on the connection (Mutex) - craeful from double locking.
27
+ def on_message
28
+ end
29
+
30
+ # # called when a disconnect is fired
31
+ # # (socket was disconnected / service should be disconnected / shutdown / socket error)
32
+ def on_disconnect
33
+ end
34
+
35
+ # called when an exception was raised
36
+ # (socket was disconnected / service should be disconnected / shutdown / socket error)
37
+ def on_exception
38
+ EventMachine.remove_io connection.io
39
+ Plezi.error e
40
+ end
41
+ end
42
+ end
43
+ end
44
+
45
+ ## Heroku/extra headers info
46
+
47
+ # All headers are considered to be case-insensitive, as per HTTP Specification.
48
+ # X-Forwarded-For: the originating IP address of the client connecting to the Heroku router
49
+ # X-Forwarded-Proto: the originating protocol of the HTTP request (example: https)
50
+ # X-Forwarded-Port: the originating port of the HTTP request (example: 443)
51
+ # X-Request-Start: unix timestamp (milliseconds) when the request was received by the router
52
+ # X-Request-Id: the Heroku HTTP Request ID
53
+ # Via: a code name for the Heroku router
54
+
@@ -0,0 +1,51 @@
1
+ module Plezi
2
+ module EventMachine
3
+ module_function
4
+
5
+ QUEUE = []
6
+ QUEUE_LOCKER = Mutex.new
7
+
8
+ def queue args, job
9
+ raise "Job missing" unless job
10
+ QUEUE_LOCKER.synchronize { QUEUE << [job, args]}
11
+ true
12
+ end
13
+
14
+ def do_job
15
+ job, args = QUEUE_LOCKER.synchronize { QUEUE.shift }
16
+ job ? (job.call(*args) || true) : false
17
+ end
18
+
19
+ def queue_empty?
20
+ QUEUE.empty?
21
+ end
22
+
23
+ end
24
+
25
+ module_function
26
+
27
+ # Accepts a block and runs it asynchronously. This method runs asynchronously and returns immediately.
28
+ #
29
+ # use:
30
+ #
31
+ # Plezi.run_async(arg1, arg2, arg3 ...) { |arg1, arg2, arg3...| do_something }
32
+ #
33
+ # the block will be run within the current context, allowing access to current methods and variables.
34
+ def run_async *args, &block
35
+ EventMachine.queue args, block
36
+ end
37
+
38
+ # This method runs asynchronously and returns immediately.
39
+ #
40
+ # This method accepts:
41
+ # object:: an object who's method will be called.
42
+ # method:: the method's name to be called. type: Symbol.
43
+ # *args:: any arguments to be passed to the method.
44
+ #
45
+ # This method also accepts an optional block which will be run with the method's returned value within the existing context.
46
+ #
47
+ def callback object, method, *args, &block
48
+ block ? EventMachine.queue( args, (proc { |ar| block.call( object.method(method).call(*ar) ) }) ) : EventMachine.queue(args, object.method(method) )
49
+ end
50
+
51
+ end
@@ -0,0 +1,138 @@
1
+ # encoding: UTF-8
2
+
3
+
4
+
5
+ module Plezi
6
+
7
+ module EventMachine
8
+
9
+ # represents an SSL connection
10
+ class SSLConnection < Connection
11
+ #the SSL socket
12
+ attr_reader :ssl_socket
13
+
14
+ def initialize socket, params
15
+ if params[:ssl] || params[:ssl_key] || params[:ssl_cert]
16
+ params[:ssl_cert], params[:ssl_key] = SSLConnection.self_cert unless params[:ssl_key] && params[:ssl_cert]
17
+ context = OpenSSL::SSL::SSLContext.new
18
+ context.set_params verify_mode: OpenSSL::SSL::VERIFY_NONE # OpenSSL::SSL::VERIFY_PEER #OpenSSL::SSL::VERIFY_NONE
19
+ # context.options DoNotReverseLookup: true
20
+ context.cert, context.key = params[:ssl_cert], params[:ssl_key]
21
+ context.cert_store = OpenSSL::X509::Store.new
22
+ context.cert_store.set_default_paths
23
+ @ssl_socket = OpenSSL::SSL::SSLSocket.new(socket, context)
24
+ @ssl_socket.sync_close = true
25
+ @ssl_socket.accept
26
+ end
27
+ raise "Not an SSL connection or SSL Socket creation failed" unless ssl_socket
28
+ super
29
+ end
30
+
31
+
32
+ # returns an IO-like object used for reading/writing (unlike the original IO object, this can be an SSL layer or any other wrapper object).
33
+ def io
34
+ @ssl_socket
35
+ end
36
+
37
+ # makes sure any data in the que is send and calls `flush` on the socket, to make sure the buffer is sent.
38
+ def flush
39
+ begin
40
+ send
41
+ @ssl_socket.flush
42
+ rescue => e
43
+
44
+ end
45
+ end
46
+
47
+ # returns true if the service is disconnected
48
+ def disconnected?
49
+ (@socket.closed? || @ssl_socket.closed? || @socket.stat.mode == 0140222) rescue true # if mode is read only, it's the same as closed.
50
+ end
51
+
52
+ # identification markers
53
+
54
+ #returns the service type - set to normal
55
+ def service_type
56
+ 'ssl'
57
+ end
58
+ #returns true if the service is encrypted using the OpenSSL library.
59
+ def ssl?
60
+ true
61
+ end
62
+
63
+ #################
64
+ # overide the followind methods for any child class.
65
+
66
+ # this is a public method and it should be used by child classes to implement each
67
+ # read(_nonblock) action. accepts one argument ::size for an optional buffer size to be read.
68
+ def read size = 1048576
69
+ data = ''
70
+ begin
71
+ loop { data << @ssl_socket.read_nonblock(size).to_s }
72
+ rescue => e
73
+
74
+ end
75
+ return false if data.to_s.empty?
76
+ touch
77
+ data
78
+ end
79
+
80
+ protected
81
+
82
+ # this is a protected method, it should be used by child classes to implement each
83
+ # send action.
84
+ def _send data
85
+ @active_time += 7200
86
+ @ssl_socket.write data
87
+ touch
88
+ end
89
+
90
+ public
91
+
92
+ # SSL certificate
93
+
94
+ # returns the current self-signed certificate - or creates a new one, if there is no current certificate.
95
+ def self.self_cert bits=2048, cn=nil, comment='a self signed certificate for when we only need encryption and no more.'
96
+ @@self_cert ||= create_cert
97
+ return *@@self_cert
98
+ end
99
+ #creates a self-signed certificate
100
+ def self.create_cert bits=2048, cn=nil, comment='a self signed certificate for when we only need encryption and no more.'
101
+ unless cn
102
+ host_name = Socket::gethostbyname(Socket::gethostname)[0].split('.')
103
+ cn = ''
104
+ host_name.each {|n| cn << "/DC=#{n}"}
105
+ cn << "/CN=#{host_name.join('.')}"
106
+ end
107
+ # cn ||= "CN=#{Socket::gethostbyname(Socket::gethostname)[0] rescue Socket::gethostname}"
108
+
109
+ rsa = OpenSSL::PKey::RSA.new(bits)
110
+ cert = OpenSSL::X509::Certificate.new
111
+ cert.version = 2
112
+ cert.serial = 1
113
+ name = OpenSSL::X509::Name.parse(cn)
114
+ cert.subject = name
115
+ cert.issuer = name
116
+ cert.not_before = Time.now
117
+ cert.not_after = Time.now + (365*24*60*60)
118
+ cert.public_key = rsa.public_key
119
+
120
+ ef = OpenSSL::X509::ExtensionFactory.new(nil,cert)
121
+ ef.issuer_certificate = cert
122
+ cert.extensions = [
123
+ ef.create_extension("basicConstraints","CA:FALSE"),
124
+ ef.create_extension("keyUsage", "keyEncipherment"),
125
+ ef.create_extension("subjectKeyIdentifier", "hash"),
126
+ ef.create_extension("extendedKeyUsage", "serverAuth"),
127
+ ef.create_extension("nsComment", comment),
128
+ ]
129
+ aki = ef.create_extension("authorityKeyIdentifier",
130
+ "keyid:always,issuer:always")
131
+ cert.add_extension(aki)
132
+ cert.sign(rsa, OpenSSL::Digest::SHA1.new)
133
+
134
+ return cert, rsa
135
+ end
136
+ end
137
+ end
138
+ end
@@ -0,0 +1,117 @@
1
+ module Plezi
2
+ module EventMachine
3
+
4
+ # Every timed event is a member of the TimedEvent class and responds to it's methods.
5
+ class TimedEvent
6
+
7
+ # Sets/gets how often a timed event repeats, in seconds.
8
+ attr_accessor :interval
9
+ # Sets/gets how many times a timed event repeats.
10
+ # If set to false or -1, the timed event will repead until the application quits.
11
+ attr_accessor :repeat_limit
12
+
13
+ # Initialize a timed event.
14
+ def initialize interval, repeat_limit = -1, args=[], job=nil
15
+ @interval = interval
16
+ @repeat_limit = repeat_limit ? repeat_limit.to_i : -1
17
+ @job = job || (Proc.new { stop! })
18
+ @next = Time.now + interval
19
+ @args = args
20
+ end
21
+
22
+ # stops a timed event.
23
+ def stop!
24
+ @repeat_limit = 0
25
+ end
26
+
27
+ # Returns true if the timer is finished.
28
+ #
29
+ # If the timed event is due, this method will also add the event to the queue.
30
+ def done?(time = Time.now)
31
+ return false unless @next <= time
32
+ return true if @repeat_limit == 0
33
+ @repeat_limit -= 1 if @repeat_limit.to_i > 0
34
+ EventMachine.queue @args, @job
35
+ @next = time + @interval
36
+ @repeat_limit == 0
37
+ end
38
+ end
39
+
40
+ module_function
41
+
42
+ # the timers stack.
43
+ TIMERS = []
44
+ # the timers stack Mutex.
45
+ TIMERS_LOCK = Mutex.new
46
+
47
+ # Creates a TimedEvent object and adds it to the Timers stack.
48
+ def timed_job seconds, limit = false, args = [], block = nil
49
+ TIMERS_LOCK.synchronize {TIMERS << TimedEvent.new(seconds, limit, args, block); TIMERS.last}
50
+ end
51
+
52
+ # returns true if there are any unhandled events
53
+ def timers?
54
+ TIMERS.any?
55
+ end
56
+
57
+ # cycles through timed jobs, executing and/or deleting them if their time has come.
58
+ def fire_timers
59
+ TIMERS_LOCK.synchronize { time = Time.now; TIMERS.delete_if {|t| t.done? time} }
60
+ end
61
+ # clears all timers
62
+ def clear_timers
63
+ TIMERS_LOCK.synchronize { TIMERS.clear }
64
+ end
65
+
66
+ end
67
+
68
+ module_function
69
+
70
+ # # returns true if there are any unhandled events
71
+ # def timers?
72
+ # EventMachine.timers?
73
+ # end
74
+
75
+ # pushes a timed event to the timers's stack
76
+ #
77
+ # accepts:
78
+ # seconds:: the minimal amount of seconds to wait before calling the handler's `call` method.
79
+ # *arg:: any arguments that will be passed to the handler's `call` method.
80
+ # &block:: the block to execute.
81
+ #
82
+ # A block is required.
83
+ #
84
+ # Timed event's time of execution is dependant on the workload and continuous uptime of the process (timed events AREN'T persistant).
85
+ def run_after seconds, *args, &block
86
+ EventMachine.timed_job seconds, 1, args, block
87
+ end
88
+
89
+ # pushes a timed event to the timers's stack
90
+ #
91
+ # accepts:
92
+ # time:: the time at which the job should be executed.
93
+ # *arg:: any arguments that will be passed to the handler's `call` method.
94
+ # &block:: the block to execute.
95
+ #
96
+ # A block is required.
97
+ #
98
+ # Timed event's time of execution is dependant on the workload and continuous uptime of the process (timed events AREN'T persistant).
99
+
100
+ def run_at time, *args, &block
101
+ EventMachine.timed_job( (Time.now - time), 1, args, block)
102
+ end
103
+ # pushes a repeated timed event to the timers's stack
104
+ #
105
+ # accepts:
106
+ # seconds:: the minimal amount of seconds to wait before calling the handler's `call` method.
107
+ # limit:: the amount of times the event should repeat itself. The event will repeat every x amount of `seconds`. The event will repeat forever if limit is set to false.
108
+ # *arg:: any arguments that will be passed to the handler's `call` method.
109
+ # &block:: the block to execute.
110
+ #
111
+ # A block is required.
112
+ #
113
+ # Timed event's time of execution is dependant on the workload and continuous uptime of the process (timed events AREN'T persistant).
114
+ def run_every seconds, limit = -1, *args, &block
115
+ EventMachine.timed_job seconds, limit, args, block
116
+ end
117
+ end
@@ -0,0 +1,35 @@
1
+ module Plezi
2
+ module EventMachine
3
+
4
+ # A single worker.
5
+ class Worker
6
+ def initialize
7
+ @stop = false
8
+ wait = Worker.get_wait
9
+ @thread = Thread.new { EventMachine.run wait until @stop }
10
+ end
11
+ def stop
12
+ @stop = true
13
+ end
14
+ def join
15
+ stop
16
+ @thread.join rescue true
17
+ end
18
+ def alive?
19
+ @thread.alive?
20
+ end
21
+ def status
22
+ @thread.status
23
+ end
24
+ def self.get_wait
25
+ @primes = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37]
26
+ @instances ||= -1
27
+ @instances += 1 if @instances < 7
28
+ @primes[@instances] / 10.0
29
+ end
30
+ def self.reset_wait
31
+ @instances = -1
32
+ end
33
+ end
34
+ end
35
+ end
@@ -13,12 +13,12 @@ module Plezi
13
13
  # parameters are the same (almost) as `add_service` and include `root` for file root, `assets`
14
14
  # and other non service related options.
15
15
  def initialize params = {}
16
- @params = params
16
+ @params = params.dup
17
17
  @routes = []
18
18
  # params[:save_assets] = true unless params[:save_assets] == false
19
- params[:index_file] ||= 'index.html'
20
- params[:assets_public] ||= '/assets'
21
- params[:assets_public].chomp! '/'
19
+ @params[:index_file] ||= 'index.html'
20
+ @params[:assets_public] ||= '/assets'
21
+ @params[:assets_public].chomp! '/'
22
22
 
23
23
  @sass_cache = Sass::CacheStores::Memory.new if defined?(::Sass)
24
24
  # @sass_cache_lock = Mutex.new
@@ -17,29 +17,9 @@ module Plezi
17
17
  elsif key.is_a?(String) && self.has_key?( key.to_sym)
18
18
  key = key.to_sym
19
19
  end
20
- @controller.response.set_cookie key, (val ? val.dup : nil) if @controller
20
+ @controller.response.set_cookie key, (val ? val.to_s.dup : nil) if @controller
21
21
  super
22
22
  end
23
23
  end
24
24
 
25
- # tweeks a hash object to read both :symbols and strings (similar to Rails but without).
26
- def self.make_hash_accept_symbols hash
27
- @magic_hash_proc ||= Proc.new do |hs,k|
28
- if k.is_a?(Symbol) && hs.has_key?( k.to_s)
29
- hs[k.to_s]
30
- elsif k.is_a?(String) && hs.has_key?( k.to_sym)
31
- hs[k.to_sym]
32
- elsif k.is_a?(Numeric) && hs.has_key?(k.to_s.to_sym)
33
- hs[k.to_s.to_sym]
34
- end
35
- end
36
- hash.default_proc = @magic_hash_proc
37
- hash.values.each do |v|
38
- if v.is_a?(Hash)
39
- make_hash_accept_symbols v
40
- end
41
- end
42
- end
43
-
44
-
45
25
  end