thin 1.5.1 → 1.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -109,7 +109,7 @@ size_t thin_http_parser_execute(http_parser *parser, const char *buffer, size_t
109
109
  pe = buffer+len;
110
110
 
111
111
  assert(*pe == '\0' && "pointer does not end on NUL");
112
- assert(pe - p == len - off && "pointers aren't same distance");
112
+ assert(pe - p == (long)(len - off) && "pointers aren't same distance");
113
113
 
114
114
 
115
115
  %% write exec;
@@ -132,6 +132,14 @@ size_t thin_http_parser_execute(http_parser *parser, const char *buffer, size_t
132
132
  return(parser->nread);
133
133
  }
134
134
 
135
+ int thin_http_parser_has_error(http_parser *parser) {
136
+ return parser->cs == http_parser_error;
137
+ }
138
+
139
+ int thin_http_parser_is_finished(http_parser *parser) {
140
+ return parser->cs == http_parser_first_final;
141
+ }
142
+
135
143
  int thin_http_parser_finish(http_parser *parser)
136
144
  {
137
145
  int cs = parser->cs;
@@ -147,11 +155,3 @@ int thin_http_parser_finish(http_parser *parser)
147
155
  return 0;
148
156
  }
149
157
  }
150
-
151
- int thin_http_parser_has_error(http_parser *parser) {
152
- return parser->cs == http_parser_error;
153
- }
154
-
155
- int thin_http_parser_is_finished(http_parser *parser) {
156
- return parser->cs == http_parser_first_final;
157
- }
@@ -71,7 +71,6 @@ DEF_MAX_LENGTH(REQUEST_PATH, 1024);
71
71
  DEF_MAX_LENGTH(QUERY_STRING, (1024 * 10));
72
72
  DEF_MAX_LENGTH(HEADER, (1024 * (80 + 32)));
73
73
 
74
-
75
74
  static void http_field(void *data, const char *field, size_t flen, const char *value, size_t vlen)
76
75
  {
77
76
  char *ch, *end;
@@ -87,10 +86,10 @@ static void http_field(void *data, const char *field, size_t flen, const char *v
87
86
  f = rb_str_buf_cat(f, field, flen);
88
87
 
89
88
  for(ch = RSTRING_PTR(f) + RSTRING_LEN(global_http_prefix), end = RSTRING_PTR(f) + RSTRING_LEN(f); ch < end; ch++) {
90
- if(*ch == '-') {
89
+ if (*ch >= 'a' && *ch <= 'z') {
90
+ *ch &= ~0x20; // upcase
91
+ } else if (*ch == '-') {
91
92
  *ch = '_';
92
- } else {
93
- *ch = toupper(*ch);
94
93
  }
95
94
  }
96
95
 
@@ -22,11 +22,18 @@ module Thin
22
22
 
23
23
  # Maximum number of connections that can be persistent
24
24
  attr_accessor :maximum_persistent_connections
25
-
25
+
26
+ #allows setting of the eventmachine threadpool size
27
+ attr_reader :threadpool_size
28
+ def threadpool_size=(size)
29
+ @threadpool_size = size
30
+ EventMachine.threadpool_size = size
31
+ end
32
+
26
33
  # Allow using threads in the backend.
27
34
  attr_writer :threaded
28
35
  def threaded?; @threaded end
29
-
36
+
30
37
  # Allow using SSL in the backend.
31
38
  attr_writer :ssl, :ssl_options
32
39
  def ssl?; @ssl end
@@ -46,6 +53,7 @@ module Thin
46
53
  @no_epoll = false
47
54
  @ssl = nil
48
55
  @threaded = nil
56
+ @started_reactor = false
49
57
  end
50
58
 
51
59
  # Start the backend and connect it.
@@ -53,6 +61,7 @@ module Thin
53
61
  @stopping = false
54
62
  starter = proc do
55
63
  connect
64
+ yield if block_given?
56
65
  @running = true
57
66
  end
58
67
 
@@ -60,6 +69,7 @@ module Thin
60
69
  if EventMachine.reactor_running?
61
70
  starter.call
62
71
  else
72
+ @started_reactor = true
63
73
  EventMachine.run(&starter)
64
74
  end
65
75
  end
@@ -81,7 +91,7 @@ module Thin
81
91
  @running = false
82
92
  @stopping = false
83
93
 
84
- EventMachine.stop if EventMachine.reactor_running?
94
+ EventMachine.stop if @started_reactor && EventMachine.reactor_running?
85
95
  @connections.each_value { |connection| connection.close_connection }
86
96
  close
87
97
  end
@@ -26,8 +26,8 @@ module Thin
26
26
  trace shell_cmd
27
27
  trap('INT') {} # Ignore INT signal to pass CTRL+C to subprocess
28
28
  Open3.popen3(shell_cmd) do |stdin, stdout, stderr|
29
- log stdout.gets until stdout.eof?
30
- log stderr.gets until stderr.eof?
29
+ log_info stdout.gets until stdout.eof?
30
+ log_info stderr.gets until stderr.eof?
31
31
  end
32
32
  end
33
33
 
@@ -10,6 +10,8 @@ module Thin
10
10
  # This is a template async response. N.B. Can't use string for body on 1.9
11
11
  AsyncResponse = [-1, {}, []].freeze
12
12
 
13
+ EMPTY_BODY = [].freeze
14
+
13
15
  # Rack application (adapter) served by this connection.
14
16
  attr_accessor :app
15
17
 
@@ -35,11 +37,10 @@ module Thin
35
37
  # Called when data is received from the client.
36
38
  def receive_data(data)
37
39
  @idle = false
38
- trace { data }
40
+ trace data
39
41
  process if @request.parse(data)
40
42
  rescue InvalidRequest => e
41
- log "!! Invalid request"
42
- log_error e
43
+ log_error("Invalid request", e)
43
44
  post_process Response::BAD_REQUEST
44
45
  end
45
46
 
@@ -81,8 +82,8 @@ module Thin
81
82
  response = @app.call(@request.env)
82
83
  end
83
84
  response
84
- rescue Exception
85
- handle_error
85
+ rescue Exception => e
86
+ unexpected_error(e)
86
87
  # Pass through error response
87
88
  can_persist? && @request.persistent? ? Response::PERSISTENT_ERROR : Response::ERROR
88
89
  end
@@ -96,19 +97,23 @@ module Thin
96
97
 
97
98
  @response.status, @response.headers, @response.body = *result
98
99
 
99
- log "!! Rack application returned nil body. Probably you wanted it to be an empty string?" if @response.body.nil?
100
+ log_error("Rack application returned nil body. " \
101
+ "Probably you wanted it to be an empty string?") if @response.body.nil?
102
+
103
+ # HEAD requests should not return a body.
104
+ @response.body = EMPTY_BODY if @request.head?
100
105
 
101
106
  # Make the response persistent if requested by the client
102
107
  @response.persistent! if @request.persistent?
103
108
 
104
109
  # Send the response
105
110
  @response.each do |chunk|
106
- trace { chunk }
111
+ trace chunk
107
112
  send_data chunk
108
113
  end
109
114
 
110
- rescue Exception
111
- handle_error
115
+ rescue Exception => e
116
+ unexpected_error(e)
112
117
  # Close connection since we can't handle response gracefully
113
118
  close_connection
114
119
  ensure
@@ -122,10 +127,9 @@ module Thin
122
127
  end
123
128
  end
124
129
 
125
- # Logs catched exception and closes the connection.
126
- def handle_error
127
- log "!! Unexpected error while processing request: #{$!.message}"
128
- log_error
130
+ # Logs information about an unexpected exceptional condition
131
+ def unexpected_error(e)
132
+ log_error("Unexpected error while processing request", e)
129
133
  end
130
134
 
131
135
  def close_request_response
@@ -192,8 +196,8 @@ module Thin
192
196
  # IP Address of the remote client.
193
197
  def remote_address
194
198
  socket_address
195
- rescue Exception
196
- log_error
199
+ rescue Exception => e
200
+ log_error('Could not infer remote address', e)
197
201
  nil
198
202
  end
199
203
 
@@ -45,7 +45,7 @@ module Thin
45
45
 
46
46
  # Start a single server
47
47
  def start_server(number)
48
- log "Starting server on #{server_id(number)} ... "
48
+ log_info "Starting server on #{server_id(number)} ... "
49
49
 
50
50
  run :start, number
51
51
  end
@@ -57,7 +57,7 @@ module Thin
57
57
 
58
58
  # Stop a single server
59
59
  def stop_server(number)
60
- log "Stopping server on #{server_id(number)} ... "
60
+ log_info "Stopping server on #{server_id(number)} ... "
61
61
 
62
62
  run :stop, number
63
63
  end
@@ -91,7 +91,7 @@ module Thin
91
91
 
92
92
  # Make sure the server is running before moving on to the next one.
93
93
  def wait_until_server_started(number)
94
- log "Waiting for server to start ..."
94
+ log_info "Waiting for server to start ..."
95
95
  STDOUT.flush # Need this to make sure user got the message
96
96
 
97
97
  tries = 0
@@ -175,4 +175,4 @@ module Thin
175
175
  end
176
176
  end
177
177
  end
178
- end
178
+ end
@@ -37,7 +37,7 @@ module Thin
37
37
  def start
38
38
  # Constantize backend class
39
39
  @options[:backend] = eval(@options[:backend], TOPLEVEL_BINDING) if @options[:backend]
40
-
40
+
41
41
  server = Server.new(@options[:socket] || @options[:address], # Server detects kind of socket
42
42
  @options[:port], # Port ignored on UNIX socket
43
43
  @options)
@@ -50,6 +50,7 @@ module Thin
50
50
  server.maximum_persistent_connections = @options[:max_persistent_conns]
51
51
  server.threaded = @options[:threaded]
52
52
  server.no_epoll = @options[:no_epoll] if server.backend.respond_to?(:no_epoll=)
53
+ server.threadpool_size = @options[:threadpool_size] if server.threaded?
53
54
 
54
55
  # ssl support
55
56
  if @options[:ssl]
@@ -113,7 +114,7 @@ module Thin
113
114
  @options.keys.each { |o| @options[o.to_s] = @options.delete(o) }
114
115
 
115
116
  File.open(config_file, 'w') { |f| f << @options.to_yaml }
116
- log ">> Wrote configuration to #{config_file}"
117
+ log_info "Wrote configuration to #{config_file}"
117
118
  end
118
119
 
119
120
  protected
@@ -165,7 +166,7 @@ module Thin
165
166
  private
166
167
  def load_adapter
167
168
  adapter = @options[:adapter] || Rack::Adapter.guess(@options[:chdir])
168
- log ">> Using #{adapter} adapter"
169
+ log_info "Using #{adapter} adapter"
169
170
  Rack::Adapter.for(adapter, @options)
170
171
  rescue Rack::AdapterNotFound => e
171
172
  raise InvalidOption, e.message
@@ -185,4 +186,4 @@ module Thin
185
186
  end
186
187
  end
187
188
  end
188
- end
189
+ end
@@ -33,11 +33,11 @@ module Thin
33
33
 
34
34
  def install(config_files_path=DEFAULT_CONFIG_PATH)
35
35
  if File.exist?(INITD_PATH)
36
- log ">> Thin service already installed at #{INITD_PATH}"
36
+ log_info "Thin service already installed at #{INITD_PATH}"
37
37
  else
38
- log ">> Installing thin service at #{INITD_PATH} ..."
38
+ log_info "Installing thin service at #{INITD_PATH} ..."
39
39
  sh "mkdir -p #{File.dirname(INITD_PATH)}"
40
- log "writing #{INITD_PATH}"
40
+ log_info "writing #{INITD_PATH}"
41
41
  File.open(INITD_PATH, 'w') do |f|
42
42
  f << ERB.new(File.read(TEMPLATE)).result(binding)
43
43
  end
@@ -46,30 +46,30 @@ module Thin
46
46
 
47
47
  sh "mkdir -p #{config_files_path}"
48
48
 
49
- log ''
50
- log "To configure thin to start at system boot:"
51
- log "on RedHat like systems:"
52
- log " sudo /sbin/chkconfig --level 345 #{NAME} on"
53
- log "on Debian-like systems (Ubuntu):"
54
- log " sudo /usr/sbin/update-rc.d -f #{NAME} defaults"
55
- log "on Gentoo:"
56
- log " sudo rc-update add #{NAME} default"
57
- log ''
58
- log "Then put your config files in #{config_files_path}"
49
+ log_info ''
50
+ log_info "To configure thin to start at system boot:"
51
+ log_info "on RedHat like systems:"
52
+ log_info " sudo /sbin/chkconfig --level 345 #{NAME} on"
53
+ log_info "on Debian-like systems (Ubuntu):"
54
+ log_info " sudo /usr/sbin/update-rc.d -f #{NAME} defaults"
55
+ log_info "on Gentoo:"
56
+ log_info " sudo rc-update add #{NAME} default"
57
+ log_info ''
58
+ log_info "Then put your config files in #{config_files_path}"
59
59
  end
60
60
 
61
61
  private
62
62
  def run(command)
63
63
  Dir[config_path + '/*'].each do |config|
64
- log "[#{command}] #{config} ..."
64
+ log_info "[#{command}] #{config} ..."
65
65
  Command.run(command, :config => config, :daemonize => true)
66
66
  end
67
67
  end
68
68
 
69
69
  def sh(cmd)
70
- log cmd
70
+ log_info cmd
71
71
  system(cmd)
72
72
  end
73
73
  end
74
74
  end
75
- end
75
+ end
@@ -55,10 +55,8 @@ module Thin
55
55
 
56
56
  write_pid_file
57
57
 
58
- self.after_daemonize if self.respond_to? :after_daemonize
59
-
60
58
  at_exit do
61
- log ">> Exiting!"
59
+ log_info "Exiting!"
62
60
  remove_pid_file
63
61
  end
64
62
  end
@@ -66,7 +64,7 @@ module Thin
66
64
  # Change privileges of the process
67
65
  # to the specified user and group.
68
66
  def change_privilege(user, group=user)
69
- log ">> Changing process privilege to #{user}:#{group}"
67
+ log_info "Changing process privilege to #{user}:#{group}"
70
68
 
71
69
  uid, gid = Process.euid, Process.egid
72
70
  target_uid = Etc.getpwnam(user).uid
@@ -82,7 +80,7 @@ module Thin
82
80
  Process::UID.change_privilege(target_uid)
83
81
  end
84
82
  rescue Errno::EPERM => e
85
- log "Couldn't change user and group to #{user}:#{group}: #{e}"
83
+ log_info "Couldn't change user and group to #{user}:#{group}: #{e}"
86
84
  end
87
85
 
88
86
  # Register a proc to be called to restart the server.
@@ -93,7 +91,7 @@ module Thin
93
91
  # Restart the server.
94
92
  def restart
95
93
  if @on_restart
96
- log '>> Restarting ...'
94
+ log_info 'Restarting ...'
97
95
  stop
98
96
  remove_pid_file
99
97
  @on_restart.call
@@ -122,7 +120,7 @@ module Thin
122
120
  # Send a +signal+ to the process which PID is stored in +pid_file+.
123
121
  def send_signal(signal, pid_file, timeout=60)
124
122
  if pid = read_pid_file(pid_file)
125
- Logging.log "Sending #{signal} signal to process #{pid} ... "
123
+ Logging.log_info "Sending #{signal} signal to process #{pid} ... "
126
124
  Process.kill(signal, pid)
127
125
  Timeout.timeout(timeout) do
128
126
  sleep 0.1 while Process.running?(pid)
@@ -131,17 +129,17 @@ module Thin
131
129
  raise PidFileNotFound, "Can't stop process, no PID found in #{pid_file}"
132
130
  end
133
131
  rescue Timeout::Error
134
- Logging.log "Timeout!"
132
+ Logging.log_info "Timeout!"
135
133
  force_kill(pid, pid_file)
136
134
  rescue Interrupt
137
135
  force_kill(pid, pid_file)
138
136
  rescue Errno::ESRCH # No such process
139
- Logging.log "process not found!"
137
+ Logging.log_info "process not found!"
140
138
  force_kill(pid, pid_file)
141
139
  end
142
140
 
143
141
  def force_kill(pid, pid_file)
144
- Logging.log "Sending KILL signal to process #{pid} ... "
142
+ Logging.log_info "Sending KILL signal to process #{pid} ... "
145
143
  Process.kill("KILL", pid)
146
144
  File.delete(pid_file) if File.exist?(pid_file)
147
145
  end
@@ -161,7 +159,7 @@ module Thin
161
159
  end
162
160
 
163
161
  def write_pid_file
164
- log ">> Writing PID to #{@pid_file}"
162
+ log_info "Writing PID to #{@pid_file}"
165
163
  open(@pid_file,"w") { |f| f.write(Process.pid) }
166
164
  File.chmod(0644, @pid_file)
167
165
  end
@@ -173,7 +171,7 @@ module Thin
173
171
  raise PidFileExist, "#{@pid_file} already exists, seems like it's already running (process ID: #{pid}). " +
174
172
  "Stop the process or delete #{@pid_file}."
175
173
  else
176
- log ">> Deleting stale PID file #{@pid_file}"
174
+ log_info "Deleting stale PID file #{@pid_file}"
177
175
  remove_pid_file
178
176
  end
179
177
  end
@@ -1,54 +1,165 @@
1
+ require 'logger'
2
+
1
3
  module Thin
2
4
  # To be included in classes to allow some basic logging
3
5
  # that can be silenced (<tt>Logging.silent=</tt>) or made
4
6
  # more verbose.
5
- # <tt>Logging.debug=</tt>: log all error backtrace and messages
6
- # logged with +debug+.
7
- # <tt>Logging.trace=</tt>: log all raw request and response and
8
- # messages logged with +trace+.
7
+ # <tt>Logging.trace=</tt>: log all raw request and response and
8
+ # messages logged with +trace+.
9
+ # <tt>Logging.silent=</tt>: silence all log all log messages
10
+ # altogether.
9
11
  module Logging
12
+ # Simple formatter which only displays the message.
13
+ # Taken from ActiveSupport
14
+ class SimpleFormatter < Logger::Formatter
15
+ def call(severity, timestamp, progname, msg)
16
+ "#{String === msg ? msg : msg.inspect}\n"
17
+ end
18
+ end
19
+
10
20
  class << self
11
- attr_writer :trace, :debug, :silent
12
-
13
- def trace?; !@silent && @trace end
14
- def debug?; !@silent && @debug end
15
- def silent?; @silent end
16
- end
17
-
18
- # Global silencer methods
21
+
22
+ attr_reader :logger
23
+ attr_reader :trace_logger
24
+
25
+ def trace=(enabled)
26
+ if enabled
27
+ @trace_logger ||= Logger.new(STDOUT)
28
+ else
29
+ @trace_logger = nil
30
+ end
31
+ end
32
+
33
+ def trace?
34
+ !@trace_logger.nil?
35
+ end
36
+
37
+ def silent=(shh)
38
+ if shh
39
+ @logger = nil
40
+ else
41
+ @logger ||= Logger.new(STDOUT)
42
+ end
43
+ end
44
+
45
+ def silent?
46
+ !@logger.nil?
47
+ end
48
+
49
+ def level
50
+ @logger ? @logger.level : nil # or 'silent'
51
+ end
52
+
53
+ def level=(value)
54
+ # If logging has been silenced, then re-enable logging
55
+ @logger = Logger.new(STDOUT) if @logger.nil?
56
+ @logger.level = value
57
+ end
58
+
59
+ # Allow user to specify a custom logger to use.
60
+ # This object must respond to:
61
+ # +level+, +level=+ and +debug+, +info+, +warn+, +error+, +fatal+
62
+ def logger=(custom_logger)
63
+ [ :level ,
64
+ :level= ,
65
+ :debug ,
66
+ :info ,
67
+ :warn ,
68
+ :error ,
69
+ :fatal ,
70
+ :unknown ,
71
+ ].each do |method|
72
+ if not custom_logger.respond_to?(method)
73
+ raise ArgumentError, "logger must respond to #{method}"
74
+ end
75
+ end
76
+
77
+ @logger = custom_logger
78
+ end
79
+
80
+ def trace_logger=(custom_tracer)
81
+ [ :level ,
82
+ :level= ,
83
+ :debug ,
84
+ :info ,
85
+ :warn ,
86
+ :error ,
87
+ :fatal ,
88
+ :unknown ,
89
+ ].each do |method|
90
+ if not custom_tracer.respond_to?(method)
91
+ raise ArgumentError, "trace logger must respond to #{method}"
92
+ end
93
+ end
94
+
95
+ @trace_logger = custom_tracer
96
+ end
97
+
98
+ def log_msg(msg, level=Logger::INFO)
99
+ return unless @logger
100
+ @logger.add(level, msg)
101
+ end
102
+
103
+ def trace_msg(msg)
104
+ return unless @trace_logger
105
+ @trace_logger.info(msg)
106
+ end
107
+
108
+ # Provided for backwards compatibility.
109
+ # Callers should be using the +level+ (on the +Logging+ module
110
+ # or on the instance) to figure out what the log level is.
111
+ def debug?
112
+ self.level == Logger::DEBUG
113
+ end
114
+ def debug=(val)
115
+ self.level = (val ? Logger::DEBUG : Logger::INFO)
116
+ end
117
+
118
+ end # module methods
119
+
19
120
  def silent
20
121
  Logging.silent?
21
122
  end
123
+
22
124
  def silent=(value)
23
125
  Logging.silent = value
24
126
  end
25
-
26
- # Log a message to the console
27
- def log(msg)
28
- puts msg unless Logging.silent?
29
- end
30
- module_function :log
31
- public :log
32
-
33
- # Log a message to the console if tracing is activated
127
+
128
+ # Log a message if tracing is activated
34
129
  def trace(msg=nil)
35
- log msg || yield if Logging.trace?
130
+ Logging.trace_msg(msg) if msg
36
131
  end
37
132
  module_function :trace
38
133
  public :trace
39
-
40
- # Log a message to the console if debugging is activated
41
- def debug(msg=nil)
42
- log msg || yield if Logging.debug?
43
- end
44
- module_function :debug
45
- public :debug
46
-
47
- # Log an error backtrace if debugging is activated
48
- def log_error(e=$!)
49
- STDERR.print("#{e}\n\t" + e.backtrace.join("\n\t") + "\n") if Logging.debug?
134
+
135
+ # Log a message at DEBUG level
136
+ def log_debug(msg=nil)
137
+ Logging.log_msg(msg || yield, Logger::DEBUG)
138
+ end
139
+ module_function :log_debug
140
+ public :log_debug
141
+
142
+ # Log a message at INFO level
143
+ def log_info(msg)
144
+ Logging.log_msg(msg || yield, Logger::INFO)
145
+ end
146
+ module_function :log_info
147
+ public :log_info
148
+
149
+ # Log a message at ERROR level (and maybe a backtrace)
150
+ def log_error(msg, e=nil)
151
+ log_msg = msg + ": #{e}\n\t" + e.backtrace.join("\n\t") + "\n" if e
152
+ Logging.log_msg(log_msg, Logger::ERROR)
50
153
  end
51
154
  module_function :log_error
52
155
  public :log_error
156
+
157
+ # For backwards compatibility
158
+ def log msg
159
+ STDERR.puts('#log has been deprecated, please use the ' \
160
+ 'log_level function instead (e.g. - log_info).')
161
+ log_info(msg)
162
+ end
163
+
53
164
  end
54
165
  end