serverengine 1.5.11 → 1.6.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.
@@ -66,12 +66,16 @@ module ServerEngine
66
66
  s = self
67
67
  SignalThread.new do |st|
68
68
  st.trap(Daemon::Signals::GRACEFUL_STOP) { s.stop(true) }
69
- st.trap(Daemon::Signals::IMMEDIATE_STOP) { s.stop(false) }
70
- st.trap(Daemon::Signals::GRACEFUL_RESTART) { s.restart(true) }
71
- st.trap(Daemon::Signals::IMMEDIATE_RESTART) { s.restart(false) }
72
- st.trap(Daemon::Signals::RELOAD) { s.reload }
73
69
  st.trap(Daemon::Signals::DETACH) { s.stop(true) }
74
- st.trap(Daemon::Signals::DUMP) { Sigdump.dump }
70
+ # Here disables signals excepting GRACEFUL_STOP == :SIGTERM because
71
+ # only SIGTERM is available on all version of Windows.
72
+ unless ServerEngine.windows?
73
+ st.trap(Daemon::Signals::IMMEDIATE_STOP) { s.stop(false) }
74
+ st.trap(Daemon::Signals::GRACEFUL_RESTART) { s.restart(true) }
75
+ st.trap(Daemon::Signals::IMMEDIATE_RESTART) { s.restart(false) }
76
+ st.trap(Daemon::Signals::RELOAD) { s.reload }
77
+ st.trap(Daemon::Signals::DUMP) { Sigdump.dump }
78
+ end
75
79
  end
76
80
  end
77
81
 
@@ -0,0 +1,170 @@
1
+ #
2
+ # ServerEngine
3
+ #
4
+ # Copyright (C) 2012-2013 Sadayuki Furuhashi
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+ module ServerEngine
19
+ module SocketManager
20
+
21
+ require 'socket'
22
+ require 'ipaddr'
23
+
24
+ class Client
25
+ def initialize(path)
26
+ @path = path
27
+ end
28
+
29
+ def listen_tcp(bind, port)
30
+ peer = connect_peer(@path)
31
+ begin
32
+ SocketManager.send_peer(peer, [Process.pid, :listen_tcp, bind, port])
33
+ res = SocketManager.recv_peer(peer)
34
+ if res.is_a?(Exception)
35
+ raise res
36
+ else
37
+ return recv_tcp(peer, res)
38
+ end
39
+ ensure
40
+ peer.close
41
+ end
42
+ end
43
+
44
+ def listen_udp(bind, port)
45
+ peer = connect_peer(@path)
46
+ begin
47
+ SocketManager.send_peer(peer, [Process.pid, :listen_udp, bind, port])
48
+ res = SocketManager.recv_peer(peer)
49
+ if res.is_a?(Exception)
50
+ raise res
51
+ else
52
+ return recv_udp(peer, res)
53
+ end
54
+ ensure
55
+ peer.close
56
+ end
57
+ end
58
+ end
59
+
60
+ class Server
61
+ def self.generate_path
62
+ if ServerEngine.windows?
63
+ for port in 10000..65535
64
+ if `netstat -na | find "#{port}"`.length == 0
65
+ return port
66
+ end
67
+ end
68
+ else
69
+ '/tmp/SERVERENGINE_SOCKETMANAGER_' + Time.now.to_s.gsub(' ', '') + '_' + Process.pid.to_s
70
+ end
71
+ end
72
+
73
+ def self.open(path)
74
+ new(path)
75
+ end
76
+
77
+ def initialize(path)
78
+ @tcp_sockets = {}
79
+ @udp_sockets = {}
80
+ @mutex = Mutex.new
81
+ @path = start_server(path)
82
+ end
83
+
84
+ attr_reader :path
85
+
86
+ def new_client
87
+ Client.new(@path)
88
+ end
89
+
90
+ def close
91
+ stop_server
92
+ nil
93
+ end
94
+
95
+ private
96
+
97
+ def listen_tcp(bind, port)
98
+ key, bind_ip = resolve_bind_key(bind, port)
99
+
100
+ @mutex.synchronize do
101
+ if @tcp_sockets.has_key?(key)
102
+ return @tcp_sockets[key]
103
+ else
104
+ return @tcp_sockets[key] = listen_tcp_new(bind_ip, port)
105
+ end
106
+ end
107
+ end
108
+
109
+ def listen_udp(bind, port)
110
+ key, bind_ip = resolve_bind_key(bind, port)
111
+
112
+ @mutex.synchronize do
113
+ if @udp_sockets.has_key?(key)
114
+ return @udp_sockets[key]
115
+ else
116
+ return @udp_sockets[key] = listen_udp_new(bind_ip, port)
117
+ end
118
+ end
119
+ end
120
+
121
+ def resolve_bind_key(bind, port)
122
+ bind_ip = IPAddr.new(IPSocket.getaddress(bind))
123
+ if bind_ip.ipv6?
124
+ return "[#{bind_ip}]:#{port}", bind_ip
125
+ else
126
+ # assuming ipv4
127
+ return "#{bind_ip}:#{port}", bind_ip
128
+ end
129
+ end
130
+
131
+ def process_peer(peer)
132
+ while true
133
+ pid, method, bind, port = *SocketManager.recv_peer(peer)
134
+ begin
135
+ send_socket(peer, pid, method, bind, port)
136
+ rescue => e
137
+ SocketManager.send_peer(peer, e)
138
+ end
139
+ end
140
+ ensure
141
+ peer.close
142
+ end
143
+ end
144
+
145
+ def self.send_peer(peer, obj)
146
+ data = Marshal.dump(obj)
147
+ peer.write [data.bytesize].pack('N')
148
+ peer.write data
149
+ end
150
+
151
+ def self.recv_peer(peer)
152
+ len = peer.read(4).unpack('N').first
153
+ data = peer.read(len)
154
+ Marshal.load(data)
155
+ end
156
+
157
+ require_relative 'utils'
158
+
159
+ if ServerEngine.windows?
160
+ require_relative 'socket_manager_win'
161
+ Client.include(SocketManagerWin::ClientModule)
162
+ Server.include(SocketManagerWin::ServerModule)
163
+ else
164
+ require_relative 'socket_manager_unix'
165
+ Client.include(SocketManagerUnix::ClientModule)
166
+ Server.include(SocketManagerUnix::ServerModule)
167
+ end
168
+
169
+ end
170
+ end
@@ -0,0 +1,96 @@
1
+ #
2
+ # ServerEngine
3
+ #
4
+ # Copyright (C) 2012-2013 Sadayuki Furuhashi
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ module ServerEngine
20
+ module SocketManagerUnix
21
+
22
+ module ClientModule
23
+ private
24
+
25
+ def connect_peer(path)
26
+ return UNIXSocket.new(path)
27
+ end
28
+
29
+ def recv_tcp(peer, sent)
30
+ return peer.recv_io(TCPServer)
31
+ end
32
+
33
+ def recv_udp(peer, sent)
34
+ return peer.recv_io(UDPSocket)
35
+ end
36
+ end
37
+
38
+ module ServerModule
39
+ private
40
+
41
+ def listen_tcp_new(bind_ip, port)
42
+ sock = TCPServer.new(bind_ip.to_s, port)
43
+ sock.listen(Socket::SOMAXCONN) # TODO make backlog configurable if necessary
44
+ return sock
45
+ end
46
+
47
+ def listen_udp_new(bind_ip, port)
48
+ if bind_ip.ipv6?
49
+ sock = UDPSocket.new(Socket::AF_INET6)
50
+ else
51
+ sock = UDPSocket.new
52
+ end
53
+ sock.bind(bind_ip.to_s, port)
54
+ return sock
55
+ end
56
+
57
+ def start_server(path)
58
+ # return absolute path so that client can connect to this path
59
+ # when client changed working directory
60
+ path = File.expand_path(path)
61
+
62
+ @server = UNIXServer.new(path)
63
+
64
+ @thread = Thread.new do
65
+ begin
66
+ while peer = @server.accept
67
+ Thread.new(peer, &method(:process_peer)) # process_peer calls send_socket
68
+ end
69
+ rescue => e
70
+ unless @server.closed?
71
+ ServerEngine.dump_uncaught_error(e)
72
+ end
73
+ end
74
+ end
75
+
76
+ return path
77
+ end
78
+
79
+ def stop_server
80
+ @tcp_sockets.reject! {|key,lsock| lsock.close; true }
81
+ @udp_sockets.reject! {|key,usock| usock.close; true }
82
+ @server.close unless @server.closed?
83
+ @thread.join
84
+ end
85
+
86
+ def send_socket(peer, pid, method, bind, port)
87
+ sock = send(method, bind, port) # calls listen_tcp or listen_udp
88
+
89
+ SocketManager.send_peer(peer, nil)
90
+
91
+ peer.send_io sock
92
+ end
93
+ end
94
+
95
+ end
96
+ end
@@ -0,0 +1,147 @@
1
+ #
2
+ # ServerEngine
3
+ #
4
+ # Copyright (C) 2012-2013 Sadayuki Furuhashi
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ module ServerEngine
20
+ module SocketManagerWin
21
+
22
+ require_relative 'winsock'
23
+
24
+ module ClientModule
25
+ private
26
+
27
+ def connect_peer(addr)
28
+ return TCPSocket.open("127.0.0.1", addr)
29
+ end
30
+
31
+ def recv_tcp(peer, sent)
32
+ proto = WinSock::WSAPROTOCOL_INFO.from_bin(sent)
33
+
34
+ handle = WinSock.WSASocketA(Socket::AF_INET, Socket::SOCK_STREAM, 0, proto, 0, 1)
35
+ if handle == WinSock::INVALID_SOCKET
36
+ RbWinSock.raise_last_error("WSASocketA(2)")
37
+ end
38
+
39
+ return RbWinSock.wrap_io_handle(TCPServer, handle, 0)
40
+ end
41
+
42
+ def recv_udp(peer, sent)
43
+ proto = WinSock::WSAPROTOCOL_INFO.from_bin(sent)
44
+
45
+ handle = WinSock.WSASocketA(Socket::AF_INET, Socket::SOCK_DGRAM, 0, proto, 0, 1)
46
+ if handle == WinSock::INVALID_SOCKET
47
+ RbWinSock.raise_last_error("WSASocketA(2)")
48
+ end
49
+
50
+ return RbWinSock.wrap_io_handle(UDPSocket, handle, 0)
51
+ end
52
+ end
53
+
54
+ module ServerModule
55
+ private
56
+
57
+ def listen_tcp_new(bind_ip, port)
58
+ sock_addr = Socket.pack_sockaddr_in(port, bind_ip.to_s)
59
+
60
+ handle = WinSock.WSASocketA(Socket::AF_INET, Socket::SOCK_STREAM, Socket::IPPROTO_TCP, nil, 0, 1)
61
+ if handle == WinSock::INVALID_SOCKET
62
+ RbWinSock.raise_last_error("WSASocketA(2)")
63
+ end
64
+
65
+ # wrap in TCPServer immediately so that its finalizer safely closes the handle
66
+ sock = RbWinSock.wrap_io_handle(TCPServer, handle, 0)
67
+
68
+ unless WinSock.bind(sock.handle, sock_addr, sock_addr.bytesize) == 0
69
+ RbWinSock.raise_last_error("bind(2)")
70
+ end
71
+ unless WinSock.listen(sock.handle, Socket::SOMAXCONN) == 0
72
+ RbWinSock.raise_last_error("listen(2)")
73
+ end
74
+
75
+ return sock
76
+ end
77
+
78
+ def listen_udp_new(bind_ip, port)
79
+ sock_addr = Socket.pack_sockaddr_in(port, bind_ip.to_s)
80
+
81
+ handle = WinSock.WSASocketA(Socket::AF_INET, Socket::SOCK_DGRAM, Socket::IPPROTO_UDP, nil, 0, 1)
82
+ if handle == WinSock::INVALID_SOCKET
83
+ RbWinSock.raise_last_error("WSASocketA(2)")
84
+ end
85
+
86
+ # wrap in UDPSocket immediately so that its finalizer safely closes the handle
87
+ sock = RbWinSock.wrap_io_handle(UDPSocket, handle, 0)
88
+
89
+ unless WinSock.bind(sock.handle, sock_addr, sock_addr.bytesize) == 0
90
+ RbWinSock.raise_last_error("bind(2)")
91
+ end
92
+
93
+ return sock
94
+ end
95
+
96
+ def htons(h)
97
+ [h].pack("S").unpack("n")[0]
98
+ end
99
+
100
+ def start_server(addr)
101
+ # TODO: use TCPServer, but this is risky because using not conflict path is easy,
102
+ # but using not conflict port is difficult. Then We had better implement using NamedPipe.
103
+ @server = TCPServer.new("127.0.0.1", addr)
104
+ @thread = Thread.new do
105
+ begin
106
+ while peer = @server.accept
107
+ Thread.new(peer, &method(:process_peer)) # process_peer calls send_socket
108
+ end
109
+ rescue => e
110
+ unless @server.closed?
111
+ ServerEngine.dump_uncaught_error(e)
112
+ end
113
+ end
114
+ end
115
+
116
+ return path
117
+ end
118
+
119
+ def stop_server
120
+ @tcp_sockets.reject! {|key,lsock| lsock.close; true }
121
+ @udp_sockets.reject! {|key,usock| usock.close; true }
122
+ @server.close unless @server.closed?
123
+ @thread.join
124
+ end
125
+
126
+ def send_socket(peer, pid, method, bind, port)
127
+ case method
128
+ when :listen_tcp
129
+ sock = listen_tcp(bind, port)
130
+ type = Socket::SOCK_STREAM
131
+ when :listen_udp
132
+ sock = listen_udp(bind, port)
133
+ type = Socket::SOCK_DGRAM
134
+ else
135
+ raise ArgumentError, "Unknown method: #{method.inspect}"
136
+ end
137
+
138
+ proto = WinSock::WSAPROTOCOL_INFO.malloc
139
+ unless WinSock.WSADuplicateSocketA(sock.handle, pid, proto) == 0
140
+ RbWinSock.raise_last_error("WSADuplicateSocketA(3)")
141
+ end
142
+
143
+ SocketManager.send_peer(peer, proto.to_bin)
144
+ end
145
+ end
146
+ end
147
+ end
@@ -46,6 +46,9 @@ module ServerEngine
46
46
  @disable_reload = !!@config[:disable_reload]
47
47
  end
48
48
 
49
+ # server is available after start_server() call.
50
+ attr_reader :server
51
+
49
52
  def reload_config
50
53
  super
51
54
 
@@ -89,7 +92,7 @@ module ServerEngine
89
92
  end
90
93
 
91
94
  def create_server(logger)
92
- @create_server_proc.call(@load_config_proc, logger)
95
+ @server = @create_server_proc.call(@load_config_proc, logger)
93
96
  end
94
97
 
95
98
  def stop(stop_graceful)