Class: Thin::Server
- Inherits:
-
Object
- Object
- Thin::Server
- Includes:
- Daemonizable, Logging
- Defined in:
- lib/thin/server.rb
Overview
The Thin HTTP server used to served request. It listen for incoming request on a given port and forward all request to all the handlers in the order they were registered. Based on HTTP 1.1 protocol specs www.w3.org/Protocols/rfc2616/rfc2616.html
Direct Known Subclasses
Instance Attribute Summary collapse
-
#handlers ⇒ Object
List of handlers to process the request in the order they are given.
-
#host ⇒ Object
Addresse and port on which the server is listening for connections.
-
#port ⇒ Object
Addresse and port on which the server is listening for connections.
-
#timeout ⇒ Object
Maximum time for a request to be red and parsed.
Attributes included from Daemonizable
Attributes included from Logging
Instance Method Summary collapse
-
#initialize(host, port, *handlers) ⇒ Server
constructor
Creates a new server binded to
host:port
that will pass request tohandlers
. -
#listen! ⇒ Object
Start listening for connections.
-
#process(client) ⇒ Object
Process one request from a client.
-
#start ⇒ Object
Starts the handlers.
-
#start! ⇒ Object
Start the server and listen for connections.
-
#stop ⇒ Object
Stop the server from accepting new request.
-
#stop! ⇒ Object
Force the server to stop right now!.
Methods included from Daemonizable
#change_privilege, #daemonize, included
Constructor Details
#initialize(host, port, *handlers) ⇒ Server
Creates a new server binded to host:port
that will pass request to handlers
.
25 26 27 28 29 30 31 32 33 34 35 36 |
# File 'lib/thin/server.rb', line 25 def initialize(host, port, *handlers) @host = host @port = port @handlers = handlers @timeout = 60 # sec, max time to read and parse a request @trace = false @stop = true # true is server is stopped @processing = false # true is processing a request @socket = TCPServer.new(host, port) end |
Instance Attribute Details
#handlers ⇒ Object
List of handlers to process the request in the order they are given.
18 19 20 |
# File 'lib/thin/server.rb', line 18 def handlers @handlers end |
#host ⇒ Object
Addresse and port on which the server is listening for connections.
15 16 17 |
# File 'lib/thin/server.rb', line 15 def host @host end |
#port ⇒ Object
Addresse and port on which the server is listening for connections.
15 16 17 |
# File 'lib/thin/server.rb', line 15 def port @port end |
#timeout ⇒ Object
Maximum time for a request to be red and parsed.
21 22 23 |
# File 'lib/thin/server.rb', line 21 def timeout @timeout end |
Instance Method Details
#listen! ⇒ Object
Start listening for connections
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
# File 'lib/thin/server.rb', line 56 def listen! @stop = false trap('INT') do log '>> Caught INT signal, stopping ...' stop end log ">> Listening on #{host}:#{port}, CTRL+C to stop" until @stop @processing = false client = @socket.accept rescue nil break if @socket.closed? || client.nil? @processing = true process(client) end ensure @socket.close unless @socket.closed? rescue nil end |
#process(client) ⇒ Object
Process one request from a client
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 113 114 115 116 117 118 119 120 121 122 123 124 125 126 |
# File 'lib/thin/server.rb', line 76 def process(client) return if client.eof? trace { 'Request started'.center(80, '=') } request = Request.new response = Response.new request.trace = @trace trace { ">> Tracing request parsing ... " } # Parse the request checking for timeout to prevent DOS attacks Timeout.timeout(@timeout) { request.parse!(client) } trace { request.raw } # Add client info to the request env request.params['REMOTE_ADDR'] = client.peeraddr.last # Add server info to the request env request.params['SERVER_SOFTWARE'] = SERVER request.params['SERVER_PORT'] = @port.to_s served = false @handlers.each do |handler| served = handler.process(request, response) break if served end if served trace { ">> Sending response:\n" + response.to_s } response.write client else client << ERROR_404_RESPONSE end trace { 'Request finished'.center(80, '=') } rescue EOFError, Errno::ECONNRESET, Errno::EPIPE, Errno::EINVAL, Errno::EBADF # Can't do anything sorry, closing the socket in the ensure block rescue InvalidRequest => e log "Invalid request: #{e.}" trace { e.backtrace.join("\n") } client << ERROR_400_RESPONSE rescue nil rescue Object => e log "Unexpected error while processing request: #{e.}" log e.backtrace.join("\n") ensure request.close if request rescue nil response.close if response rescue nil client.close unless client.closed? rescue nil end |
#start ⇒ Object
Starts the handlers.
39 40 41 42 43 44 45 46 47 |
# File 'lib/thin/server.rb', line 39 def start log ">> Thin web server (v#{VERSION::STRING})" trace ">> Tracing ON" @handlers.each do |handler| log ">> Starting #{handler} ..." handler.start end end |
#start! ⇒ Object
Start the server and listen for connections
50 51 52 53 |
# File 'lib/thin/server.rb', line 50 def start! start listen! end |
#stop ⇒ Object
Stop the server from accepting new request. If a request is processing, wait for this to finish and shutdown the server.
131 132 133 134 |
# File 'lib/thin/server.rb', line 131 def stop @stop = true stop! unless @processing # Not processing a request, so we can stop now end |
#stop! ⇒ Object
Force the server to stop right now!
137 138 139 |
# File 'lib/thin/server.rb', line 137 def stop! @socket.close rescue nil # break the accept loop by closing the socket end |