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