crap_server 0.0.2.2 → 0.0.3.0
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.
- checksums.yaml +4 -4
- data/README.md +2 -9
- data/lib/crap_server/application.rb +11 -41
- data/lib/crap_server/configure.rb +1 -0
- data/lib/crap_server/connection_handler.rb +158 -0
- data/lib/crap_server/connection_instance.rb +16 -30
- data/lib/crap_server/forker.rb +108 -0
- data/lib/crap_server/version.rb +1 -1
- data/lib/crap_server.rb +2 -1
- metadata +4 -3
- data/lib/crap_server/helpers/socket_reader.rb +0 -35
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3b7a2133b068b88f831896a41664b94448243b84
|
4
|
+
data.tar.gz: 49f6bebeaf9bdb5b51e33a39053630bc47068b40
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9bb7f5661dd2b9b51d96abf188a4038a275ae77a3ff13d4315b6c0ac03c616354bbc91293f8a65df6f2157c01616bc4432fbf1d841143cd7286e8163f3577414
|
7
|
+
data.tar.gz: 694265fae58d630a42b6b973014590fe1f3ec66e29b300a42f2919d9691b6e987154878e08b5ac6bd980032d38ccdc6358d4614bab3663606c4cf2cb86ce12e3
|
data/README.md
CHANGED
@@ -3,6 +3,7 @@
|
|
3
3
|
[](https://codeclimate.com/github/anga/crap_server)
|
4
4
|
|
5
5
|
Really thin and non intuitive ruby server. Made to be fast and ready for really heavy servers (not only http server).
|
6
|
+
Use Preforking and Evented pattern.
|
6
7
|
|
7
8
|
# Another one?
|
8
9
|
|
@@ -35,14 +36,6 @@ Or install it yourself as:
|
|
35
36
|
end
|
36
37
|
end
|
37
38
|
|
38
|
-
## Configuring the app
|
39
|
-
|
40
|
-
CrapServer::Application.configure do |config|
|
41
|
-
config.port = 80
|
42
|
-
config.read_method = :partial
|
43
|
-
config.read_buffer_size = 1024 # 1K
|
44
|
-
end
|
45
|
-
|
46
39
|
See all available options in lib/crap_server/configure.rb
|
47
40
|
|
48
41
|
# Running our application
|
@@ -51,7 +44,7 @@ ruby my_app.rb
|
|
51
44
|
|
52
45
|
# Production ready?
|
53
46
|
|
54
|
-
No.
|
47
|
+
No. Use it under your own risk. Right now, the interface can change.
|
55
48
|
|
56
49
|
## Contributing
|
57
50
|
|
@@ -24,7 +24,6 @@ module CrapServer
|
|
24
24
|
|
25
25
|
# Main method. This setup all the connections and make the logic of the app
|
26
26
|
def run!(&block)
|
27
|
-
|
28
27
|
begin
|
29
28
|
# Bup the maximum opened file to the maximum allowed by the system
|
30
29
|
Process.setrlimit(:NOFILE, Process.getrlimit(:NOFILE)[1])
|
@@ -40,11 +39,12 @@ module CrapServer
|
|
40
39
|
logger.debug "Maximum number of allowed connections: #{Process.getrlimit(:NOFILE)[1]}" # Same as maximum of opened files
|
41
40
|
logger.info ''
|
42
41
|
|
43
|
-
#
|
44
|
-
|
45
|
-
|
46
|
-
|
42
|
+
# Prefork and handle the connections in each process.
|
43
|
+
forker = CrapServer::Forker.new([socket_ipv4, socket_ipv6])
|
44
|
+
# Run loop. (basically, waiting until Ctrl+C)
|
45
|
+
forker.run &block
|
47
46
|
|
47
|
+
# NOTE: I think this line never will be executed
|
48
48
|
close_connections
|
49
49
|
|
50
50
|
# If any kind of error happens, we MUST close the sockets
|
@@ -61,38 +61,6 @@ module CrapServer
|
|
61
61
|
end
|
62
62
|
|
63
63
|
protected
|
64
|
-
def connection_loop(remote_socket, addres_info, &block)
|
65
|
-
# Work with the connection...
|
66
|
-
if we_should_read?
|
67
|
-
reader = CrapServer::Helpers::SocketReader.new(remote_socket, config.method)
|
68
|
-
reader.address = addres_info
|
69
|
-
reader.config = config
|
70
|
-
reader.on_message(&block)
|
71
|
-
else
|
72
|
-
begin
|
73
|
-
if block.parameters == 1
|
74
|
-
block.call(remote_socket)
|
75
|
-
else
|
76
|
-
block.call(remote_socket, addres_info)
|
77
|
-
end
|
78
|
-
# If we get out of data to read (but still having an opened connection), we wait for new data.
|
79
|
-
rescue IO::WaitReadable
|
80
|
-
# This, prevent to execute so many retry and block the code until a new bunch of data gets available
|
81
|
-
IO.select([remote_socket])
|
82
|
-
# Yay!, we have more data. Now we can continue!
|
83
|
-
retry
|
84
|
-
end
|
85
|
-
end
|
86
|
-
# ...
|
87
|
-
|
88
|
-
# Close the connection
|
89
|
-
remote_socket.close if config.auto_close_connection
|
90
|
-
end
|
91
|
-
|
92
|
-
# Return true or false if the read process is done by the server.
|
93
|
-
def we_should_read?
|
94
|
-
not config.manual_read
|
95
|
-
end
|
96
64
|
|
97
65
|
# Open TCP connection (IPv4 and IPv6)
|
98
66
|
def open_connections
|
@@ -107,14 +75,14 @@ module CrapServer
|
|
107
75
|
# If any kind of error happens, we MUST close the sockets
|
108
76
|
if socket_ipv4
|
109
77
|
# Shuts down communication on all copies of the connection.
|
110
|
-
socket_ipv4.shutdown
|
111
|
-
socket_ipv4.close
|
78
|
+
# socket_ipv4.shutdown
|
79
|
+
# socket_ipv4.close
|
112
80
|
end
|
113
81
|
|
114
82
|
if socket_ipv6
|
115
83
|
# Shuts down communication on all copies of the connection.
|
116
|
-
socket_ipv6.shutdown
|
117
|
-
socket_ipv6.close
|
84
|
+
# socket_ipv6.shutdown
|
85
|
+
# socket_ipv6.close
|
118
86
|
end
|
119
87
|
# TODO: Close all opened sockets connections from other threads and processes
|
120
88
|
end
|
@@ -137,6 +105,7 @@ module CrapServer
|
|
137
105
|
# Tell to the Kernel that is ok to rebind the port if is in TIME_WAIT state (after close the connection
|
138
106
|
# and the Kernel wait for client acknowledgement)
|
139
107
|
socket_ipv6.setsockopt(:SOCKET, :REUSEADDR, true)
|
108
|
+
|
140
109
|
@socket6 = socket_ipv6
|
141
110
|
end
|
142
111
|
|
@@ -158,6 +127,7 @@ module CrapServer
|
|
158
127
|
# Tell to the Kernel that is ok to rebind the port if is in TIME_WAIT state (after close the connection
|
159
128
|
# and the Kernel wait for client acknowledgement)
|
160
129
|
socket_ipv4.setsockopt(:SOCKET, :REUSEADDR, true)
|
130
|
+
|
161
131
|
@socket4 = socket_ipv4
|
162
132
|
end
|
163
133
|
|
@@ -22,6 +22,7 @@ module CrapServer
|
|
22
22
|
attr_accessor :method
|
23
23
|
# Set to false if you want to manage the close of the connection.
|
24
24
|
# Note that this require manual_read set to true.
|
25
|
+
# DEPERCATED
|
25
26
|
attr_accessor :auto_close_connection
|
26
27
|
# The file to use as log
|
27
28
|
attr_accessor :log_file
|
@@ -0,0 +1,158 @@
|
|
1
|
+
module CrapServer
|
2
|
+
class ConnectionHandler
|
3
|
+
|
4
|
+
def initialize(sockets)
|
5
|
+
@sockets = sockets
|
6
|
+
@sockets.each do |io|
|
7
|
+
add_to_read io
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def add_to_write(io)
|
12
|
+
@to_write ||= {}
|
13
|
+
@to_write[io.fileno] = io
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_write
|
17
|
+
(@to_write ||= {}).values
|
18
|
+
end
|
19
|
+
|
20
|
+
def remove_to_write(io)
|
21
|
+
@to_write.delete io.fileno
|
22
|
+
@buffer.delete io.fileno
|
23
|
+
end
|
24
|
+
|
25
|
+
def buffer(io)
|
26
|
+
@buffer ||= {}
|
27
|
+
@buffer[io.fileno]
|
28
|
+
end
|
29
|
+
|
30
|
+
def set_buffer(io, string)
|
31
|
+
@buffer ||= {}
|
32
|
+
@buffer[io.fileno] = string
|
33
|
+
end
|
34
|
+
|
35
|
+
def to_read
|
36
|
+
(@to_read ||= {}).values
|
37
|
+
end
|
38
|
+
|
39
|
+
def add_to_read(io)
|
40
|
+
@to_read ||= {}
|
41
|
+
@to_read[io.fileno] = io
|
42
|
+
end
|
43
|
+
|
44
|
+
def remove_to_read(io)
|
45
|
+
@to_read.delete io.fileno
|
46
|
+
@address.delete io.fileno
|
47
|
+
end
|
48
|
+
|
49
|
+
def address(io)
|
50
|
+
@address ||= {}
|
51
|
+
@address[io.fileno]
|
52
|
+
end
|
53
|
+
|
54
|
+
def set_address(io, addrs)
|
55
|
+
@address ||= {}
|
56
|
+
@address[io.fileno] = addrs
|
57
|
+
end
|
58
|
+
|
59
|
+
def read_buffer(io)
|
60
|
+
@rbuffer ||= {}
|
61
|
+
@rbuffer[io.fileno]
|
62
|
+
end
|
63
|
+
|
64
|
+
def add_read_buffer(io, string)
|
65
|
+
@rbuffer ||= {}
|
66
|
+
@rbuffer[io.fileno] ||= ''
|
67
|
+
@rbuffer[io.fileno] << string
|
68
|
+
end
|
69
|
+
|
70
|
+
def set_close_after_write(io)
|
71
|
+
@closeaw ||= {}
|
72
|
+
@closeaw[io.fileno] = true
|
73
|
+
end
|
74
|
+
|
75
|
+
def close_after_write(io)
|
76
|
+
@closeaw ||= {}
|
77
|
+
@closeaw[io.fileno]
|
78
|
+
end
|
79
|
+
|
80
|
+
def close(io)
|
81
|
+
remove_to_read io
|
82
|
+
remove_to_write io
|
83
|
+
@closeaw.delete io.fileno
|
84
|
+
io.close
|
85
|
+
end
|
86
|
+
|
87
|
+
def handle(&block)
|
88
|
+
# The main loop. Listening IPv4 and IPv6 connections
|
89
|
+
accept_loop do |data, remote_socket, address_info|
|
90
|
+
instance = CrapServer::ConnectionInstance.new
|
91
|
+
instance.socket = remote_socket
|
92
|
+
instance.config = config
|
93
|
+
instance.address = address_info
|
94
|
+
instance.send(:method=, config.method)
|
95
|
+
instance.handler = self
|
96
|
+
instance.run data, &block
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
protected
|
101
|
+
|
102
|
+
# Evented loop (Reactor pattern)
|
103
|
+
def accept_loop
|
104
|
+
loop {
|
105
|
+
@readables, @writables = IO.select(to_read, to_write)
|
106
|
+
|
107
|
+
@readables.each do |socket|
|
108
|
+
if @sockets.include? socket
|
109
|
+
io, addr = socket.accept
|
110
|
+
set_address io, addr
|
111
|
+
set_close_after_write io if config.auto_close_connection
|
112
|
+
# Disabling Nagle's algorithm. Is fucking slow :P
|
113
|
+
io.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
|
114
|
+
# We add him to the read queue
|
115
|
+
add_to_read io
|
116
|
+
else
|
117
|
+
begin
|
118
|
+
_, data = socket, socket.read_nonblock(config.read_buffer_size)
|
119
|
+
yield data, socket, address(socket)
|
120
|
+
rescue Errno::EAGAIN
|
121
|
+
rescue EOFError
|
122
|
+
remove_to_read socket
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
@writables.each do |socket|
|
128
|
+
begin
|
129
|
+
string = buffer socket
|
130
|
+
bytes = socket.write_nonblock string
|
131
|
+
string.slice! 0, bytes
|
132
|
+
if string.empty?
|
133
|
+
# If we don't have more data to send to the client
|
134
|
+
if close_after_write socket
|
135
|
+
close socket
|
136
|
+
else
|
137
|
+
remove_to_write socket
|
138
|
+
end
|
139
|
+
else
|
140
|
+
set_buffer socket, string
|
141
|
+
remove_to_read socket
|
142
|
+
end
|
143
|
+
# If the client close the connection, we remove is from read and from write
|
144
|
+
rescue Errno::ECONNRESET, Errno::EPIPE
|
145
|
+
if close_after_write socket
|
146
|
+
close socket
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
}
|
151
|
+
end
|
152
|
+
|
153
|
+
protected
|
154
|
+
def config
|
155
|
+
CrapServer::Application.send(:config)
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
@@ -6,57 +6,43 @@ module CrapServer
|
|
6
6
|
attr_accessor :address
|
7
7
|
attr_accessor :config
|
8
8
|
attr_accessor :method
|
9
|
+
attr_accessor :handler
|
9
10
|
def initialize; end
|
10
11
|
|
11
12
|
# This method execute the block sent to run! method
|
12
|
-
def run(&block)
|
13
|
+
def run(data, &block)
|
13
14
|
# Undefine the last definition if was defined
|
14
15
|
undef :call if self.respond_to? :call
|
15
16
|
# Define the new method to bind the block with this class.
|
16
17
|
self.class.send :define_method, :call, &block
|
17
18
|
# Running the code depending of the number of args
|
18
19
|
if block.parameters.size == 1
|
19
|
-
self.call(
|
20
|
+
self.call(data)
|
20
21
|
elsif block.parameters.size == 2
|
21
|
-
self.call(
|
22
|
+
self.call(data, socket)
|
22
23
|
else
|
23
|
-
self.call(
|
24
|
+
self.call(data, socket, address)
|
24
25
|
end
|
26
|
+
@socket.flush
|
25
27
|
end
|
26
28
|
|
27
29
|
# Write to the client the given string
|
28
30
|
def write(string)
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
31
|
+
@handler.add_to_write @socket
|
32
|
+
@handler.set_buffer @socket, string
|
33
|
+
end
|
34
|
+
|
35
|
+
def close_after_write
|
36
|
+
@handler.set_close_after_write @socket
|
37
|
+
end
|
38
|
+
|
39
|
+
def close
|
40
|
+
@handler.close @socket
|
38
41
|
end
|
39
42
|
|
40
43
|
# Give access to logger class to the user
|
41
44
|
def logger
|
42
45
|
@config.logger
|
43
46
|
end
|
44
|
-
protected
|
45
|
-
# Read the data from the socket
|
46
|
-
def read_data
|
47
|
-
begin
|
48
|
-
# Read the data from the socket
|
49
|
-
if @method == :normal
|
50
|
-
@socket.read(config.read_buffer_size)
|
51
|
-
elsif @method == :partial
|
52
|
-
@socket.readpartial(config.read_buffer_size)
|
53
|
-
elsif @method == :non_blocking
|
54
|
-
@socket.read_nonblock(config.read_buffer_size)
|
55
|
-
end
|
56
|
-
rescue Errno::EAGAIN
|
57
|
-
IO.select([connection],nil,nil, config.timeout)
|
58
|
-
retry
|
59
|
-
end
|
60
|
-
end
|
61
47
|
end
|
62
48
|
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
module CrapServer
|
2
|
+
# Handle preforking task.
|
3
|
+
# Will spawn 1 process per core.
|
4
|
+
class Forker
|
5
|
+
def initialize(sockets)
|
6
|
+
@sockets = sockets
|
7
|
+
end
|
8
|
+
|
9
|
+
# Initialize
|
10
|
+
def run(&block)
|
11
|
+
begin
|
12
|
+
@block_proc = block
|
13
|
+
child_pids = []
|
14
|
+
processor_count.times do
|
15
|
+
child_pids << spawn_child
|
16
|
+
end
|
17
|
+
|
18
|
+
# We take care of our children. If someone kill one, me made another one.
|
19
|
+
# PS: Is a hard work :P
|
20
|
+
loop do
|
21
|
+
pid = Process.wait
|
22
|
+
child_pids.delete(pid)
|
23
|
+
child_pids << spawn_child
|
24
|
+
end
|
25
|
+
# If someone kill us, we kill our children. Yes, is sad, but we must do it :'(
|
26
|
+
rescue Interrupt
|
27
|
+
child_pids.each do |cpid|
|
28
|
+
begin
|
29
|
+
# We send Ctrl+C to the process
|
30
|
+
Process.kill(:INT, cpid)
|
31
|
+
rescue Errno::ESRCH
|
32
|
+
end
|
33
|
+
end
|
34
|
+
@sockets.each do |socket|
|
35
|
+
# Shuts down communication on all copies of the connection.
|
36
|
+
socket.shutdown
|
37
|
+
socket.close
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
protected
|
44
|
+
|
45
|
+
def spawn_child
|
46
|
+
fork do
|
47
|
+
begin
|
48
|
+
handler = CrapServer::ConnectionHandler.new @sockets
|
49
|
+
handler.handle &@block_proc
|
50
|
+
rescue Interrupt
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# Extracted from https://github.com/grosser/parallel/blob/master/lib/parallel.rb
|
56
|
+
# Number of processors seen by the OS and used for process scheduling.
|
57
|
+
#
|
58
|
+
# * AIX: /usr/sbin/pmcycles (AIX 5+), /usr/sbin/lsdev
|
59
|
+
# * BSD: /sbin/sysctl
|
60
|
+
# * Cygwin: /proc/cpuinfo
|
61
|
+
# * Darwin: /usr/bin/hwprefs, /usr/sbin/sysctl
|
62
|
+
# * HP-UX: /usr/sbin/ioscan
|
63
|
+
# * IRIX: /usr/sbin/sysconf
|
64
|
+
# * Linux: /proc/cpuinfo
|
65
|
+
# * Minix 3+: /proc/cpuinfo
|
66
|
+
# * Solaris: /usr/sbin/psrinfo
|
67
|
+
# * Tru64 UNIX: /usr/sbin/psrinfo
|
68
|
+
# * UnixWare: /usr/sbin/psrinfo
|
69
|
+
#
|
70
|
+
def processor_count
|
71
|
+
@processor_count ||= begin
|
72
|
+
os_name = RbConfig::CONFIG["target_os"]
|
73
|
+
if os_name =~ /mingw|mswin/
|
74
|
+
require 'win32ole'
|
75
|
+
result = WIN32OLE.connect("winmgmts://").ExecQuery(
|
76
|
+
"select NumberOfLogicalProcessors from Win32_Processor")
|
77
|
+
result.to_enum.collect(&:NumberOfLogicalProcessors).reduce(:+)
|
78
|
+
elsif File.readable?("/proc/cpuinfo")
|
79
|
+
IO.read("/proc/cpuinfo").scan(/^processor/).size
|
80
|
+
elsif File.executable?("/usr/bin/hwprefs")
|
81
|
+
IO.popen("/usr/bin/hwprefs thread_count").read.to_i
|
82
|
+
elsif File.executable?("/usr/sbin/psrinfo")
|
83
|
+
IO.popen("/usr/sbin/psrinfo").read.scan(/^.*on-*line/).size
|
84
|
+
elsif File.executable?("/usr/sbin/ioscan")
|
85
|
+
IO.popen("/usr/sbin/ioscan -kC processor") do |out|
|
86
|
+
out.read.scan(/^.*processor/).size
|
87
|
+
end
|
88
|
+
elsif File.executable?("/usr/sbin/pmcycles")
|
89
|
+
IO.popen("/usr/sbin/pmcycles -m").read.count("\n")
|
90
|
+
elsif File.executable?("/usr/sbin/lsdev")
|
91
|
+
IO.popen("/usr/sbin/lsdev -Cc processor -S 1").read.count("\n")
|
92
|
+
elsif File.executable?("/usr/sbin/sysconf") and os_name =~ /irix/i
|
93
|
+
IO.popen("/usr/sbin/sysconf NPROC_ONLN").read.to_i
|
94
|
+
elsif File.executable?("/usr/sbin/sysctl")
|
95
|
+
IO.popen("/usr/sbin/sysctl -n hw.ncpu").read.to_i
|
96
|
+
elsif File.executable?("/sbin/sysctl")
|
97
|
+
IO.popen("/sbin/sysctl -n hw.ncpu").read.to_i
|
98
|
+
else
|
99
|
+
$stderr.puts "Unknown platform: " + RbConfig::CONFIG["target_os"]
|
100
|
+
$stderr.puts "Assuming 1 processor."
|
101
|
+
1
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
|
107
|
+
end
|
108
|
+
end
|
data/lib/crap_server/version.rb
CHANGED
data/lib/crap_server.rb
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
require 'crap_server/version'
|
2
2
|
require 'crap_server/configure'
|
3
|
+
require 'crap_server/forker'
|
4
|
+
require 'crap_server/connection_handler'
|
3
5
|
require 'crap_server/connection_instance'
|
4
|
-
require 'crap_server/helpers/socket_reader'
|
5
6
|
require 'crap_server/application'
|
6
7
|
module CrapServer
|
7
8
|
# Your code goes here...
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: crap_server
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andres Jose Borek
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-08-
|
11
|
+
date: 2014-08-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -55,8 +55,9 @@ files:
|
|
55
55
|
- lib/crap_server.rb
|
56
56
|
- lib/crap_server/application.rb
|
57
57
|
- lib/crap_server/configure.rb
|
58
|
+
- lib/crap_server/connection_handler.rb
|
58
59
|
- lib/crap_server/connection_instance.rb
|
59
|
-
- lib/crap_server/
|
60
|
+
- lib/crap_server/forker.rb
|
60
61
|
- lib/crap_server/version.rb
|
61
62
|
- spec/application_spec.rb
|
62
63
|
- spec/spec_helper.rb
|
@@ -1,35 +0,0 @@
|
|
1
|
-
module CrapServer
|
2
|
-
module Helpers
|
3
|
-
# Makes easier work with sockets and read.
|
4
|
-
class SocketReader
|
5
|
-
attr_accessor :method
|
6
|
-
attr_accessor :socket
|
7
|
-
attr_accessor :address
|
8
|
-
attr_accessor :config
|
9
|
-
def initialize(socket_, method_=:partial)
|
10
|
-
@socket = socket_
|
11
|
-
@method = method_
|
12
|
-
end
|
13
|
-
|
14
|
-
def on_message(&block)
|
15
|
-
begin
|
16
|
-
instance = CrapServer::ConnectionInstance.new
|
17
|
-
instance.socket = socket
|
18
|
-
instance.config = config
|
19
|
-
instance.address = address
|
20
|
-
instance.send(:method=, method)
|
21
|
-
instance.run &block
|
22
|
-
# If we get out of data to read (but still having an opened connection), we wait for new data.
|
23
|
-
rescue IO::WaitReadable
|
24
|
-
# This, prevent to execute so many retry and block the code until a new bunch of data gets available
|
25
|
-
IO.select([@socket])
|
26
|
-
# Yay!, we have more data. Now we can continue!
|
27
|
-
retry
|
28
|
-
# When we use non_blocking method, and the client close the connection we will get EOF after that moment
|
29
|
-
# We do nothing special in that moment
|
30
|
-
rescue EOFError
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|