public
Description: A very fast & simple Ruby web server
Homepage: http://code.macournoyer.com/thin/
Clone URL: git://github.com/macournoyer/thin.git
Search Repo:
Server now let current connections finish before stopping, fixes #18
macournoyer (author)
Tue Jan 29 21:41:55 -0800 2008
commit  b6aa2a6e55acd25c77c217759789d5d9e8c2bc93
tree    a7ac5e39326cf220e44ec07387b418965c03ce10
parent  dc607a67b9931ab6f1d9026e24e3311b49466c2f
...
1
 
2
3
4
...
1
2
3
4
5
0
@@ -1,4 +1,5 @@
0
 == 0.6.2 Rambo release
0
+ * Server now let current connections finish before stopping, fixes #18
0
  * Fix uploading hanging bug when body is moved to a tempfile,
0
    also delete the tempfile properly upon completion, fixes #25
0
  * 'thin restart' now sends HUP signals rather then stopping & starting, closes #17
...
10
11
12
 
 
 
13
14
15
...
46
47
48
 
 
 
 
49
50
51
...
10
11
12
13
14
15
16
17
18
...
49
50
51
52
53
54
55
56
57
58
0
@@ -10,6 +10,9 @@
0
     # +true+ if the connection is on a UNIX domain socket.
0
     attr_accessor :unix_socket
0
     
0
+ # Server owning the connection
0
+ attr_accessor :server
0
+
0
     def post_init
0
       @request = Request.new
0
       @response = Response.new
0
@@ -46,6 +49,10 @@
0
     ensure
0
       @request.close rescue nil
0
       @response.close rescue nil
0
+ end
0
+
0
+ def unbind
0
+ @server.connection_finished(self)
0
     end
0
     
0
     protected
...
1
2
3
4
5
6
7
8
9
...
42
43
44
45
 
46
47
48
 
 
49
50
51
 
 
 
52
53
54
55
56
57
58
59
...
72
73
74
75
76
77
78
79
80
81
 
82
83
84
85
 
 
86
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
87
 
88
89
90
91
92
93
94
95
96
 
 
 
97
98
99
100
101
...
117
118
119
 
120
121
122
123
 
 
124
125
126
127
 
 
 
 
 
 
 
 
 
 
128
129
130
...
1
 
 
 
2
3
4
5
6
...
39
40
41
 
42
43
 
 
44
45
46
 
 
47
48
49
50
51
52
53
54
55
56
57
...
70
71
72
 
 
 
 
 
 
 
73
74
75
76
 
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
 
 
103
104
 
 
 
 
105
106
107
108
109
110
111
112
...
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
0
@@ -1,7 +1,4 @@
0
 module Thin
0
- # Raise when we require the server to stop
0
- class StopServer < Exception; end
0
-
0
   # The Thin HTTP server used to served request.
0
   # It listen for incoming request on a given port
0
   # and forward all request to +app+.
0
0
0
@@ -42,13 +39,14 @@
0
     #
0
     def initialize(host_or_socket, port=3000, app=nil, &block)
0
       if host_or_socket.include?('/')
0
- @socket = host_or_socket
0
+ @socket = host_or_socket
0
       else
0
- @host = host_or_socket
0
- @port = port.to_i
0
+ @host = host_or_socket
0
+ @port = port.to_i
0
       end
0
- @app = app
0
- @timeout = 60 # sec
0
+ @app = app
0
+ @timeout = 60 # sec
0
+ @connections = []
0
       
0
       @app = Rack::Builder.new(&block).to_app if block
0
     end
0
0
0
0
0
0
@@ -72,28 +70,41 @@
0
       log ">> Thin web server (v#{VERSION::STRING} codename #{VERSION::CODENAME})"
0
       trace ">> Tracing ON"
0
       
0
- EventMachine.run do
0
- begin
0
- start_server
0
- rescue StopServer
0
- stop
0
- end
0
- end
0
+ EventMachine.run { @signature = start_server }
0
     end
0
     alias :start! :start
0
     
0
- # Stops the server by stopping the listening loop.
0
+ # Stops the server after processing all current connections.
0
+ # Calling twice is the equivalent of calling <tt>stop!</tt>.
0
     def stop
0
+ if @stopping
0
+ stop!
0
+ else
0
+ @stopping = true
0
+
0
+ # Do not accept anymore connection
0
+ EventMachine.stop_server(@signature)
0
+
0
+ unless wait_for_connections_and_stop
0
+ # Still some connections running, schedule a check later
0
+ EventMachine.add_periodic_timer(1) { wait_for_connections_and_stop }
0
+ end
0
+ end
0
+ end
0
+
0
+ # Stops the server closing all current connections
0
+ def stop!
0
+ log ">> Stopping ..."
0
+
0
+ @connections.each { |connection| connection.close_connection }
0
       EventMachine.stop
0
+
0
       remove_socket_file
0
- rescue
0
- warn "Error stopping : #{$!}"
0
     end
0
     
0
- # Stops the server by raising an error.
0
- def stop!
0
- raise StopServer
0
- end
0
+ def connection_finished(connection)
0
+ @connections.delete(connection)
0
+ end
0
     
0
     protected
0
       def start_server
0
0
0
@@ -117,14 +128,27 @@
0
       end
0
       
0
       def initialize_connection(connection)
0
+ connection.server = self
0
         connection.comm_inactivity_timeout = @timeout
0
         connection.app = @app
0
         connection.silent = @silent
0
         connection.unix_socket = !@socket.nil?
0
+
0
+ @connections << connection
0
       end
0
       
0
       def remove_socket_file
0
         File.delete(@socket) if @socket && File.exist?(@socket)
0
+ end
0
+
0
+ def wait_for_connections_and_stop
0
+ if @connections.empty?
0
+ stop!
0
+ true
0
+ else
0
+ log ">> Waiting for #{@connections.size} connection(s) to finish, CTRL+C to force stop"
0
+ false
0
+ end
0
       end
0
   end
0
 end
...
10
11
12
13
14
15
 
 
 
16
17
 
18
19
20
21
22
...
23
24
25
26
 
27
28
29
30
31
32
33
 
34
35
36
37
 
38
39
40
41
...
58
59
60
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
61
 
62
63
64
...
67
68
69
70
71
 
 
72
73
74
75
...
143
144
145
146
 
147
148
149
150
 
151
152
153
...
10
11
12
 
 
 
13
14
15
16
 
17
18
19
20
21
22
...
23
24
25
 
26
27
28
29
30
31
32
 
33
34
35
36
 
37
38
39
40
41
...
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
...
100
101
102
 
 
103
104
105
106
107
108
...
176
177
178
 
179
180
181
182
183
184
185
186
187
0
@@ -10,11 +10,11 @@
0
       body << env['rack.input'].read
0
       [200, { 'Content-Type' => 'text/html', 'Content-Length' => body.size.to_s }, body]
0
     end
0
- server = Thin::Server.new('0.0.0.0', 3333, app)
0
- server.timeout = 3
0
- server.silent = true
0
+ @server = Thin::Server.new('0.0.0.0', 3333, app)
0
+ @server.timeout = 3
0
+ @server.silent = true
0
     
0
- @thread = Thread.new { server.start }
0
+ @thread = Thread.new { @server.start }
0
     sleep 0.1 until @thread.status == 'sleep'
0
   end
0
     
0
0
0
@@ -23,18 +23,18 @@
0
   end
0
   
0
   it 'should GET from TCPSocket' do
0
- raw('0.0.0.0', 3333, "GET /?this HTTP/1.1\r\n\r\n").
0
+ raw("GET /?this HTTP/1.1\r\n\r\n").
0
       should include("HTTP/1.1 200 OK",
0
                      "Content-Type: text/html", "Content-Length: ",
0
                      "Connection: close", "this")
0
   end
0
   
0
   it 'should return empty string on incomplete headers' do
0
- raw('0.0.0.0', 3333, "GET /?this HTTP/1.1\r\nHost:").should be_empty
0
+ raw("GET /?this HTTP/1.1\r\nHost:").should be_empty
0
   end
0
   
0
   it 'should return empty string on incorrect Content-Length' do
0
- raw('0.0.0.0', 3333, "POST / HTTP/1.1\r\nContent-Length: 300\r\n\r\naye").should be_empty
0
+ raw("POST / HTTP/1.1\r\nContent-Length: 300\r\n\r\naye").should be_empty
0
   end
0
   
0
   it 'should POST from Net::HTTP' do
0
0
@@ -58,7 +58,40 @@
0
     get('/').should include('"REMOTE_ADDR"=>"127.0.0.1"')
0
   end
0
   
0
+ it "should wait for current requests before soft stopping" do
0
+ socket = TCPSocket.new('0.0.0.0', 3333)
0
+ socket.write("GET / HTTP/1.1")
0
+ @server.stop # Stop the server in the middle of a request
0
+ socket.write("\r\n\r\n")
0
+
0
+ out = socket.read
0
+ socket.close
0
+
0
+ out.should_not be_empty
0
+ end
0
+
0
+ it "should not accept new requests when soft stopping" do
0
+ socket = TCPSocket.new('0.0.0.0', 3333)
0
+ socket.write("GET / HTTP/1.1")
0
+ @server.stop # Stop the server in the middle of a request
0
+
0
+ EventMachine.next_tick do
0
+ proc { get('/') }.should raise_error(Errno::ECONNRESET)
0
+ end
0
+
0
+ socket.close
0
+ end
0
+
0
+ it "should drop current requests when hard stopping" do
0
+ socket = TCPSocket.new('0.0.0.0', 3333)
0
+ socket.write("GET / HTTP/1.1")
0
+ @server.stop! # Force stop the server in the middle of a request
0
+
0
+ EventMachine.next_tick { socket.should be_closed }
0
+ end
0
+
0
   after do
0
+ @server.stop!
0
     @thread.kill
0
   end
0
   
0
@@ -67,8 +100,8 @@
0
       Net::HTTP.get(URI.parse('http://0.0.0.0:3333' + url))
0
     end
0
     
0
- def raw(host, port, data)
0
- socket = TCPSocket.new(host, port)
0
+ def raw(data)
0
+ socket = TCPSocket.new('0.0.0.0', 3333)
0
       socket.write data
0
       out = socket.read
0
       socket.close
0
0
@@ -143,11 +176,12 @@
0
   end
0
   
0
   it "should remove socket file after server stops" do
0
- @server.stop
0
+ @server.stop!
0
     File.exist?('/tmp/thin_test.sock').should be_false
0
   end
0
   
0
   after do
0
+ @server.stop!
0
     @thread.kill
0
   end
0
   

Comments

    No one has commented yet.