plezi 0.7.7 → 0.8.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +26 -0
- data/README.md +3 -3
- data/lib/plezi.rb +35 -36
- data/lib/plezi/common/cache.rb +94 -0
- data/lib/plezi/{base → common}/dsl.rb +87 -68
- data/lib/plezi/{base → common}/logging.rb +15 -15
- data/lib/plezi/eventmachine/connection.rb +192 -0
- data/lib/plezi/eventmachine/em.rb +95 -0
- data/lib/plezi/eventmachine/io.rb +265 -0
- data/lib/plezi/eventmachine/protocol.rb +54 -0
- data/lib/plezi/eventmachine/queue.rb +51 -0
- data/lib/plezi/eventmachine/ssl_connection.rb +138 -0
- data/lib/plezi/eventmachine/timers.rb +117 -0
- data/lib/plezi/eventmachine/workers.rb +35 -0
- data/lib/plezi/handlers/http_host.rb +4 -4
- data/lib/plezi/handlers/magic_helpers.rb +1 -21
- data/lib/plezi/handlers/route.rb +3 -3
- data/lib/plezi/server/{helpers/http.rb → http.rb} +1 -57
- data/lib/plezi/server/{protocols/http_protocol.rb → http_protocol.rb} +18 -35
- data/lib/plezi/server/{protocols/http_request.rb → http_request.rb} +1 -1
- data/lib/plezi/server/{protocols/http_response.rb → http_response.rb} +45 -22
- data/lib/plezi/server/{helpers/mime_types.rb → mime_types.rb} +0 -0
- data/lib/plezi/server/{protocols/websocket.rb → websocket.rb} +34 -37
- data/lib/plezi/server/{protocols/ws_response.rb → ws_response.rb} +37 -19
- data/lib/plezi/version.rb +1 -1
- data/plezi.gemspec +3 -3
- data/resources/config.ru +9 -11
- metadata +27 -30
- data/lib/plezi/base/cache.rb +0 -89
- data/lib/plezi/base/connections.rb +0 -33
- data/lib/plezi/base/engine.rb +0 -77
- data/lib/plezi/base/events.rb +0 -93
- data/lib/plezi/base/io_reactor.rb +0 -62
- data/lib/plezi/base/rack_app.rb +0 -89
- data/lib/plezi/base/services.rb +0 -57
- data/lib/plezi/base/timers.rb +0 -71
- data/lib/plezi/server/README.md +0 -33
- data/lib/plezi/server/services/basic_service.rb +0 -224
- data/lib/plezi/server/services/no_service.rb +0 -196
- 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
|