serverengine 1.5.11 → 1.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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)