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
@@ -1,196 +0,0 @@
1
- module Plezi
2
-
3
- # this class is a basic TCP socket service.
4
- #
5
- # a protocol should be assigned, or the service will fall back to an echo service.
6
- #
7
- # a protocol should answer to: on_connect(service), on_message(service, data), on_disconnect(service) and on_exception(service, exception)
8
- #
9
- # the on_message method should return any data that wasn't used (to be sent again as part of the next `on_message` call, once more data is received).
10
- #
11
- # if the protocol is a class, these methods should be instance methods.
12
- # a protocol class should support the initialize(service, parameters={}) method as well.
13
- #
14
- # to-do: fix logging
15
- class NoService
16
-
17
- # create a listener (io) - will create a TCPServer socket
18
- #
19
- # listeners are 'server sockets' that answer to `accept` by creating a new connection socket (io).
20
- def self.create_service port, parameters
21
- self
22
- end
23
-
24
- def self.accept
25
- false
26
- end
27
-
28
- # instance methods
29
-
30
- attr_reader :socket, :locker, :closed, :parameters, :out_que, :active_time
31
- attr_accessor :protocol, :handler, :timeout
32
-
33
- # creates a new connection wrapper object for the new socket that was recieved from the `accept_nonblock` method call.
34
- def initialize socket, parameters = {}
35
- @handler = parameters[:handler]
36
- @socket = nil
37
- # socket.setsockopt Socket::SOL_SOCKET, Socket::SO_RCVTIMEO, "\n\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" #== [10 sec 0 usec].pack '1_2'
38
- @out_que = []
39
- @locker = Mutex.new
40
- @parameters = parameters
41
- @protocol = parameters[:protocol]
42
- @protocol = protocol.new self, parameters if protocol.is_a?(Class)
43
- @protocol.on_connect self if @protocol && @protocol.methods.include?(:on_connect)
44
- touch
45
- @timeout ||= 5
46
- # Plezi.callback self, :on_message
47
- end
48
-
49
- # # sets a connection timeout
50
- # def set_timeout timeout = 8
51
- # @timeout = timeout
52
- # end
53
-
54
- # checks if a connection timed out
55
- def timedout?
56
- Time.now - @active_time > @timeout
57
- end
58
-
59
- # resets the timer for the connection timeout
60
- def touch
61
- @active_time = Time.now
62
- end
63
-
64
- # 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).
65
- def io
66
- touch
67
- @socket
68
- end
69
-
70
- # sends data immidiately - forcing the data to be sent, flushing any pending messages in the que
71
- def send data = nil
72
- touch
73
- end
74
-
75
- # sends data immidiately, interrupting any pending que and ignoring thread safety.
76
- def send_unsafe_interrupt data = nil
77
- touch
78
- _send data rescue disconnect
79
- end
80
-
81
- # sends data without waiting - data might be sent in a different order then intended.
82
- def send_nonblock data
83
- touch
84
- locker.synchronize {@out_que << data}
85
- Plezi.callback(self, :send)
86
- end
87
-
88
- # adds data to the out buffer - but doesn't send the data until a send event is called.
89
- def << data
90
- touch
91
- locker.synchronize {@out_que << data}
92
- end
93
-
94
- # makes sure any data in the que is send and calls `flush` on the socket, to make sure the buffer is sent.
95
- def flush
96
- end
97
-
98
- # event based interface for messages.
99
-
100
- # notice: since it is all async evet base - multipart messages might be garbled up...?
101
- # todo: protect from garbeling.
102
- def on_message
103
- # return false if locker.locked?
104
- return false if locker.locked?
105
- return disconnect if (_disconnected? rescue true)
106
- locker.synchronize do
107
- begin
108
- touch
109
- if protocol
110
- protocol.on_message self
111
- end
112
- rescue Exception => e
113
- Plezi.error e
114
- return disconnect
115
- end
116
- end
117
- end
118
-
119
- # called once a socket is disconnected or needs to be disconnected.
120
- def on_disconnect
121
- Plezi.callback Plezi, :remove_connection, self
122
-
123
- close
124
- end
125
-
126
- # status markers
127
-
128
- # closes the connection
129
- def close
130
- end
131
- # returns true if the service is disconnected
132
- def disconnected?
133
- false
134
- end
135
- # disconects the service.
136
- def disconnect
137
- on_disconnect
138
- end
139
- # returns true if the socket has content to be read.
140
- def has_incoming_data?
141
- false
142
- end
143
-
144
-
145
- # identification markers
146
-
147
- #returns the service type - set to normal
148
- def service_type
149
- 'no-service'
150
- end
151
- #returns true if the service is encrypted using the OpenSSL library.
152
- def ssl?
153
- false
154
- end
155
-
156
- #################
157
- # overide the followind methods for any child class.
158
-
159
- # this is a public method and it should be used by child classes to implement each
160
- # read(_nonblock) action. accepts one argument ::size for an optional buffer size to be read.
161
- def read size = 1048576
162
- ''
163
- end
164
-
165
- protected
166
-
167
- # this is a protected method, it should be used by child classes to implement each
168
- # send action.
169
- def _send data
170
- ''
171
- end
172
- # this is a protected method, it should be used by child classes to implement each
173
- # close action. doesn't accept any arguments.
174
- def _close
175
- end
176
- # this is a protected method, it should be used by child classes to tell if the socket
177
- # was closed (returns true/false).
178
- def _disconnected?
179
- false # if mode is read only, it's the same as closed.
180
- end
181
-
182
- end
183
- end
184
-
185
-
186
- ######
187
- ## example requests
188
-
189
- # GET / HTTP/1.1
190
- # Host: localhost:2000
191
- # Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
192
- # Cookie: user_token=2INa32_vDgx8Aa1qe43oILELpSdIe9xwmT8GTWjkS-w
193
- # User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10) AppleWebKit/600.1.25 (KHTML, like Gecko) Version/8.0 Safari/600.1.25
194
- # Accept-Language: en-us
195
- # Accept-Encoding: gzip, deflate
196
- # Connection: keep-alive
@@ -1,193 +0,0 @@
1
- module Plezi
2
-
3
- # test connections with: openssl s_client -connect localhost:3000
4
-
5
- # this class is a basic TCP socket service with SSL.
6
- #
7
- # a protocol should be assigned, or the service will fall back to an echo service.
8
- #
9
- # to-do: fix self certificate issue (fails).
10
- class SSLService < BasicService
11
-
12
- # instance methods
13
-
14
- attr_reader :ssl_socket
15
-
16
- # creates a new connection wrapper object for the new socket that was recieved from the `accept_nonblock` method call.
17
- def initialize soc, parameters = {}
18
- context = OpenSSL::SSL::SSLContext.new
19
- context.set_params verify_mode: OpenSSL::SSL::VERIFY_NONE# OpenSSL::SSL::VERIFY_PEER #OpenSSL::SSL::VERIFY_NONE
20
- # context.options DoNotReverseLookup: true
21
- if parameters[:ssl_cert] && parameters[:ssl_key]
22
- context.cert = parameters[:ssl_cert]
23
- context.key = parameters[:ssl_key]
24
- else
25
- context.cert, context.key = self.class.self_cert
26
- end
27
- context.cert_store = OpenSSL::X509::Store.new
28
- context.cert_store.set_default_paths
29
- @ssl_socket = OpenSSL::SSL::SSLSocket.new(soc, context)
30
- @ssl_socket.sync_close = true
31
- @socket = soc
32
- @ssl_socket.accept
33
- super
34
- end
35
- # identification markers
36
-
37
- #returns the service type - set to normal
38
- def service_type
39
- 'encrypted'
40
- end
41
- #returns true if the service is encrypted using the OpenSSL library.
42
- def ssl?
43
- true
44
- end
45
-
46
- # 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).
47
- def io
48
- touch
49
- @ssl_socket
50
- end
51
-
52
- # reads from the connection
53
- def read size = 1048576
54
- data = ''
55
- begin
56
- loop { data << ssl_socket.read_nonblock( size) }
57
- rescue Exception => e
58
-
59
- end
60
- touch unless data.empty?
61
- data
62
- end
63
-
64
- protected
65
-
66
- #sends data over the connection
67
- def _send data
68
- ssl_socket.write data
69
- end
70
-
71
- #closes the connection
72
- def _close
73
- ssl_socket.flush rescue true
74
- ssl_socket.close
75
- end
76
-
77
- # checks if the connection is closed
78
- def _disconnected?
79
- ssl_socket.closed? || ssl_socket.io.closed? rescue true # || ssl_socket.io.stat.mode == 0140222 <= if mode is read only, it's the same as closed.
80
- end
81
-
82
-
83
- # SSL certificate
84
-
85
- # returns the current self-signed certificate - or creates a new one, if there is no current certificate.
86
- def self.self_cert bits=2048, cn=nil, comment='a self signed certificate for when we only need encryption and no more.'
87
- @@self_cert ||= create_cert
88
- return *@@self_cert
89
- end
90
- #creates a self-signed certificate
91
- def self.create_cert bits=2048, cn=nil, comment='a self signed certificate for when we only need encryption and no more.'
92
- unless cn
93
- host_name = Socket::gethostbyname(Socket::gethostname)[0].split('.')
94
- cn = ''
95
- host_name.each {|n| cn << "/DC=#{n}"}
96
- cn << "/CN=#{host_name.join('.')}"
97
- end
98
- # cn ||= "CN=#{Socket::gethostbyname(Socket::gethostname)[0] rescue Socket::gethostname}"
99
-
100
- rsa = OpenSSL::PKey::RSA.new(bits)
101
- cert = OpenSSL::X509::Certificate.new
102
- cert.version = 2
103
- cert.serial = 1
104
- name = OpenSSL::X509::Name.parse(cn)
105
- cert.subject = name
106
- cert.issuer = name
107
- cert.not_before = Time.now
108
- cert.not_after = Time.now + (365*24*60*60)
109
- cert.public_key = rsa.public_key
110
-
111
- ef = OpenSSL::X509::ExtensionFactory.new(nil,cert)
112
- ef.issuer_certificate = cert
113
- cert.extensions = [
114
- ef.create_extension("basicConstraints","CA:FALSE"),
115
- ef.create_extension("keyUsage", "keyEncipherment"),
116
- ef.create_extension("subjectKeyIdentifier", "hash"),
117
- ef.create_extension("extendedKeyUsage", "serverAuth"),
118
- ef.create_extension("nsComment", comment),
119
- ]
120
- aki = ef.create_extension("authorityKeyIdentifier",
121
- "keyid:always,issuer:always")
122
- cert.add_extension(aki)
123
- cert.sign(rsa, OpenSSL::Digest::SHA1.new)
124
-
125
- return cert, rsa
126
- end
127
-
128
- # def self.cert_test
129
- # #Creating a CA
130
- # root_key = OpenSSL::PKey::RSA.new 2048 # the CA's public/private key
131
- # root_ca = OpenSSL::X509::Certificate.new
132
- # root_ca.version = 2 # cf. RFC 5280 - to make it a "v3" certificate
133
- # root_ca.serial = 1
134
- # root_ca.subject = OpenSSL::X509::Name.parse "/DC=org/DC=ruby-lang/CN=Ruby CA"
135
- # root_ca.issuer = root_ca.subject # root CA's are "self-signed"
136
- # root_ca.public_key = root_key.public_key
137
- # root_ca.not_before = Time.now
138
- # root_ca.not_after = root_ca.not_before + 2 * 365 * 24 * 60 * 60 # 2 years validity
139
- # ef = OpenSSL::X509::ExtensionFactory.new
140
- # ef.subject_certificate = root_ca
141
- # ef.issuer_certificate = root_ca
142
- # root_ca.add_extension(ef.create_extension("basicConstraints","CA:TRUE",true))
143
- # root_ca.add_extension(ef.create_extension("keyUsage","keyCertSign, cRLSign", true))
144
- # root_ca.add_extension(ef.create_extension("subjectKeyIdentifier","hash",false))
145
- # root_ca.add_extension(ef.create_extension("authorityKeyIdentifier","keyid:always",false))
146
- # root_ca.sign(root_key, OpenSSL::Digest::SHA256.new)
147
- # #Creating an End-Point Certificate
148
- # key = OpenSSL::PKey::RSA.new 2048
149
- # cert = OpenSSL::X509::Certificate.new
150
- # cert.version = 2
151
- # cert.serial = 2
152
- # cert.subject = OpenSSL::X509::Name.parse "/DC=org/DC=ruby-lang/CN=Ruby certificate"
153
- # cert.issuer = root_ca.subject # root CA is the issuer
154
- # cert.public_key = key.public_key
155
- # cert.not_before = Time.now
156
- # cert.not_after = cert.not_before + 1 * 365 * 24 * 60 * 60 # 1 years validity
157
- # ef = OpenSSL::X509::ExtensionFactory.new
158
- # ef.subject_certificate = cert
159
- # ef.issuer_certificate = root_ca
160
- # cert.add_extension extension_factory.create_extension('keyUsage', 'keyEncipherment,dataEncipherment,digitalSignature')
161
- # cert.add_extension(ef.create_extension("subjectKeyIdentifier","hash",false))
162
- # cert.sign(root_key, OpenSSL::Digest::SHA256.new)
163
-
164
- # # #Creating a Certificate
165
- # # name = OpenSSL::X509::Name.parse 'CN=localhost/DC=localhost'
166
- # # cert = OpenSSL::X509::Certificate.new
167
- # # cert.version = 2
168
- # # cert.serial = 0
169
- # # cert.not_before = Time.now
170
- # # cert.not_after = Time.now + 3600
171
- # # key = OpenSSL::PKey::RSA.new 2048
172
- # # cert.public_key = key.public_key
173
- # # cert.subject = name
174
-
175
- # # # Certificate Extensions
176
- # # cert.add_extension extension_factory.create_extension('basicConstraints', 'CA:FALSE', true)
177
- # # cert.add_extension extension_factory.create_extension('keyUsage', 'keyEncipherment,dataEncipherment,digitalSignature')
178
- # # cert.add_extension extension_factory.create_extension('subjectKeyIdentifier', 'hash')
179
-
180
- # # # Signing a Certificate
181
- # # cert.issuer = name
182
- # # cert.sign key, OpenSSL::Digest::SHA1.new
183
-
184
- # #server
185
- # context = OpenSSL::SSL::SSLContext.new
186
- # context.cert = cert
187
- # context.key = key
188
- # tcp_server = TCPServer.new 8080
189
- # ssl_server = OpenSSL::SSL::SSLServer.new tcp_server, context
190
- # ssl_socket = ssl_server.accept
191
- # end
192
- end
193
- end