thin 1.5.1 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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