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:
Add more doc on Server.
Refactor more connection logic into the Connectors.
Put back running? state into the server.
macournoyer (author)
Tue Feb 05 20:26:06 -0800 2008
commit  d768ff30186e03a0e4167c48cc9d3c6ba55514d8
tree    137cd68fee7c5757fe28193bbca44d2ae61e451a
parent  6283c1164dffe934e4d84803a1b8201a00e679dd
...
15
16
17
 
18
19
20
...
36
37
38
 
 
 
 
 
39
40
41
...
15
16
17
18
19
20
21
...
37
38
39
40
41
42
43
44
45
46
47
0
@@ -15,6 +15,7 @@ module Thin
0
       
0
       def initialize
0
         @connections = []
0
+ @timeout = 60 # sec
0
       end
0
             
0
       # Free up resources used by the connector.
0
@@ -36,6 +37,11 @@ module Thin
0
         @connections << connection
0
       end
0
       
0
+ # Close all active connections.
0
+ def close_connections
0
+ @connections.each { |connection| connection.close_connection }
0
+ end
0
+
0
       # Called by a connection when it's unbinded.
0
       def connection_finished(connection)
0
         @connections.delete(connection)
...
20
21
22
23
24
25
26
27
28
 
29
30
31
...
20
21
22
 
 
 
 
 
 
23
24
25
26
0
@@ -20,12 +20,7 @@ module Thin
0
       def disconnect
0
         EventMachine.stop_server(@signature)
0
       end
0
-
0
- # Returns +true+ if connected to the server
0
- def running?
0
- !@signature.nil?
0
- end
0
-
0
+
0
       def to_s
0
         "#{@host}:#{@port}"
0
       end
...
27
28
29
30
31
32
33
34
35
36
37
...
40
41
42
43
44
45
46
47
48
49
50
51
 
 
 
 
 
 
 
 
 
52
53
54
...
27
28
29
 
 
 
 
 
30
31
32
...
35
36
37
 
 
 
 
 
 
 
 
 
38
39
40
41
42
43
44
45
46
47
48
49
0
@@ -27,11 +27,6 @@ module Thin
0
         remove_socket_file
0
       end
0
       
0
- # Returns +true+ if connected to the server
0
- def running?
0
- !@signature.nil?
0
- end
0
-
0
       def to_s
0
         @socket
0
       end
0
@@ -40,14 +35,14 @@ module Thin
0
         def remove_socket_file
0
           File.delete(@socket) if @socket && File.exist?(@socket)
0
         end
0
- end
0
-
0
- class UnixConnection < Connection
0
- protected
0
- def remote_address
0
- # FIXME not sure about this, does it even make sense on a UNIX socket?
0
- Socket.unpack_sockaddr_un(get_peername)
0
- end
0
- end
0
+ end
0
+ end
0
+
0
+ class UnixConnection < Connection
0
+ protected
0
+ def remote_address
0
+ # FIXME not sure about this, does it even make sense on a UNIX socket?
0
+ Socket.unpack_sockaddr_un(get_peername)
0
+ end
0
   end
0
 end
0
\ No newline at end of file
...
1
2
3
 
 
4
5
6
7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8
9
10
11
12
13
 
14
15
16
17
18
19
 
 
20
21
22
...
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
 
 
 
 
 
 
 
 
 
49
 
50
51
52
53
 
54
 
55
56
57
 
 
 
 
 
 
 
 
58
59
60
61
62
 
 
 
 
63
64
65
...
73
74
75
 
76
77
78
79
 
80
 
 
81
82
83
84
85
86
87
88
 
 
89
90
91
...
94
95
96
 
 
97
98
99
100
 
 
 
 
101
102
103
104
105
106
 
107
108
109
...
114
115
116
 
 
 
117
118
 
119
120
121
...
1
 
 
2
3
4
5
 
 
6
7
8
9
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
42
43
44
45
46
47
48
 
49
50
51
52
53
54
 
55
56
57
58
59
...
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
98
99
100
101
...
109
110
111
112
113
114
115
116
117
118
119
120
121
122
 
 
 
 
 
 
123
124
125
126
127
...
130
131
132
133
134
135
136
137
 
138
139
140
141
142
 
 
143
144
 
145
146
147
148
...
153
154
155
156
157
158
159
 
160
161
162
163
0
@@ -1,22 +1,59 @@
0
 module Thin
0
- # The Thin HTTP server used to served request.
0
- # It listen for incoming request on a given port
0
+ # The uterly famous Thin HTTP server.
0
+ # It listen for incoming request through a given connector
0
   # and forward all request to +app+.
0
   #
0
- # Based on HTTP 1.1 protocol specs:
0
- # http://www.w3.org/Protocols/rfc2616/rfc2616.html
0
+ # == TCP server
0
+ # Create a new TCP server on bound to <tt>host:port</tt> by specifiying +host+
0
+ # and +port+ as the first 2 arguments.
0
+ #
0
+ # Thin::Server.start('0.0.0.0', 3000, app)
0
+ #
0
+ # == UNIX domain server
0
+ # Create a new UNIX domain socket bound to +socket+ file by specifiying a filename
0
+ # as the first argument. Eg.: /tmp/thin.sock. If the first argument contains a <tt>/</tt>
0
+ # it will be assumed to be a UNIX socket.
0
+ #
0
+ # Thin::Server.start('/tmp/thin.sock', nil, app)
0
+ #
0
+ # == Using a custom connector
0
+ # You can implement your own way to connect the server to its client by creating your
0
+ # own Thin::Connectors::Connector class and pass it as the first argument.
0
+ #
0
+ # connector = Thin::Connectors::MyFancyConnector.new('galaxy://faraway:1345')
0
+ # Thin::Server.start(connector, nil, app)
0
+ #
0
+ # == Rack application (+app+)
0
+ # All requests will be processed through +app+ that must be a valid Rack adapter.
0
+ # A valid Rack adapter (application) must respond to <tt>call(env#Hash)</tt> and
0
+ # return an array of <tt>[status, headers, body]</tt>.
0
+ #
0
+ # == Building an app in place
0
+ # If a block is passed, a <tt>Rack::Builder</tt> instance
0
+ # will be passed to build the +app+. So you can do cool stuff like this:
0
+ #
0
+ # Thin::Server.start('0.0.0.0', 3000) do
0
+ # use Rack::CommonLogger
0
+ # use Rack::ShowExceptions
0
+ # map "/lobster" do
0
+ # use Rack::Lint
0
+ # run Rack::Lobster.new
0
+ # end
0
+ # end
0
+ #
0
   class Server
0
     include Logging
0
     include Daemonizable
0
     extend Forwardable
0
         
0
- # App called with the request that produces the response.
0
+ # Application (Rack adapter) called with the request that produces the response.
0
     attr_accessor :app
0
     
0
     # Connector handling the connections to the clients.
0
     attr_accessor :connector
0
     
0
- # Maximum time for incoming data to arrive
0
+ # Maximum number of seconds for incoming data to arrive before the connection
0
+ # is dropped.
0
     def_delegators :@connector, :timeout, :timeout=
0
     
0
     # Address and port on which the server is listening for connections.
0
@@ -25,41 +62,40 @@ module Thin
0
     # UNIX domain socket on which the server is listening for connections.
0
     def_delegator :@connector, :socket
0
     
0
- # Creates a new server bound to <tt>host:port</tt>
0
- # or to +socket+ that will pass request to +app+.
0
- # If +host_or_socket+ contains a <tt>/</tt> it is assumed
0
- # to be a UNIX domain socket filename.
0
- # If a block is passed, a <tt>Rack::Builder</tt> instance
0
- # will be passed to build the +app+.
0
- #
0
- # Server.new '0.0.0.0', 3000 do
0
- # use Rack::CommonLogger
0
- # use Rack::ShowExceptions
0
- # map "/lobster" do
0
- # use Rack::Lint
0
- # run Rack::Lobster.new
0
- # end
0
- # end.start
0
- #
0
- def initialize(host_or_socket, port=3000, app=nil, &block)
0
- if host_or_socket.include?('/')
0
- @connector = Connectors::UnixServer.new(host_or_socket)
0
- else
0
- @connector = Connectors::TcpServer.new(host_or_socket, port.to_i)
0
+ def initialize(host_or_socket_or_connector, port=3000, app=nil, &block)
0
+ # Try to intelligently select which connector to use.
0
+ @connector = case
0
+ when host_or_socket_or_connector.is_a?(Connectors::Connector)
0
+ host_or_socket_or_connector
0
+ when host_or_socket_or_connector.include?('/')
0
+ Connectors::UnixServer.new(host_or_socket_or_connector)
0
+ else
0
+ Connectors::TcpServer.new(host_or_socket_or_connector, port.to_i)
0
       end
0
+
0
       @connector.server = self
0
- @app = app
0
- @timeout = 60 # sec
0
- @connections = []
0
+ @app = app
0
       
0
+ # Allow using Rack builder as a block
0
       @app = Rack::Builder.new(&block).to_app if block
0
     end
0
     
0
+ # Lil' shortcut to turn this:
0
+ #
0
+ # Server.new(...).start
0
+ #
0
+ # into this:
0
+ #
0
+ # Server.start(...)
0
+ #
0
     def self.start(*args, &block)
0
       new(*args, &block).start!
0
     end
0
     
0
- # Start the server and listen for connections
0
+ # Start the server and listen for connections.
0
+ # Also register signals:
0
+ # * INT calls +stop+ to shutdown gracefully.
0
+ # * TERM calls <tt>stop!</tt> to force shutdown.
0
     def start
0
       raise ArgumentError, 'app required' unless @app
0
       
0
@@ -73,19 +109,19 @@ module Thin
0
       trace ">> Tracing ON"
0
       
0
       log ">> Listening on #{@connector}, CTRL+C to stop"
0
+ @running = true
0
       EventMachine.run { @connector.connect }
0
     end
0
     alias :start! :start
0
     
0
+ # == Gracefull shutdown.
0
     # Stops the server after processing all current connections.
0
+ # As soon as this method is called, the server stops accepting
0
+ # new requests and wait for all current connections to finish.
0
     # Calling twice is the equivalent of calling <tt>stop!</tt>.
0
     def stop
0
- return unless running?
0
-
0
- if @stopping
0
- stop!
0
- else
0
- @stopping = true
0
+ if @running
0
+ @running = false
0
         
0
         # Do not accept anymore connection
0
         @connector.disconnect
0
@@ -94,16 +130,19 @@ module Thin
0
           # Still some connections running, schedule a check later
0
           EventMachine.add_periodic_timer(1) { wait_for_connections_and_stop }
0
         end
0
+ else
0
+ stop!
0
       end
0
     end
0
     
0
- # Stops the server closing all current connections
0
+ # == Force shuting
0
+ # Stops the server closing all current connections right away.
0
+ # This doesn't wait for connection to finish their work and send data.
0
+ # All current requests will be dropped.
0
     def stop!
0
- return unless running?
0
-
0
       log ">> Stopping ..."
0
 
0
- @connections.each { |connection| connection.close_connection }
0
+ @connector.close_connections
0
       EventMachine.stop
0
 
0
       @connector.close
0
@@ -114,8 +153,11 @@ module Thin
0
     end
0
     alias :to_s :name
0
     
0
+ # Return +true+ if the server is running and ready to receive requests.
0
+ # Note that the server might still be running and return +false+ when
0
+ # shuting down and waiting for active connections to complete.
0
     def running?
0
- @connector.running?
0
+ @running
0
     end
0
     
0
     protected

Comments

    No one has commented yet.