Class: Thin::Server

Inherits:
Object
  • Object
show all
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

RailsServer

Instance Attribute Summary collapse

Attributes included from Daemonizable

#log_file, #pid_file

Attributes included from Logging

#silent

Instance Method Summary collapse

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

#handlersObject

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

#hostObject

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

#portObject

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

#timeoutObject

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.message}"
  trace { e.backtrace.join("\n") }
  client << ERROR_400_RESPONSE rescue nil
rescue Object => e
  log "Unexpected error while processing request: #{e.message}"
  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

#startObject

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

#stopObject

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