friendlyfashion-thin 1.4.1 → 1.4.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -13,30 +13,30 @@ module Thin
13
13
  class Base
14
14
  # Server serving the connections throught the backend
15
15
  attr_accessor :server
16
-
16
+
17
17
  # Maximum time for incoming data to arrive
18
18
  attr_accessor :timeout
19
-
19
+
20
20
  # Maximum number of file or socket descriptors that the server may open.
21
21
  attr_accessor :maximum_connections
22
-
22
+
23
23
  # Maximum number of connections that can be persistent
24
24
  attr_accessor :maximum_persistent_connections
25
-
25
+
26
26
  # Allow using threads in the backend.
27
27
  attr_writer :threaded
28
28
  def threaded?; @threaded end
29
-
29
+
30
30
  # Allow using SSL in the backend.
31
31
  attr_writer :ssl, :ssl_options
32
32
  def ssl?; @ssl end
33
-
33
+
34
34
  # Number of persistent connections currently opened
35
35
  attr_accessor :persistent_connection_count
36
-
36
+
37
37
  # Disable the use of epoll under Linux
38
38
  attr_accessor :no_epoll
39
-
39
+
40
40
  def initialize
41
41
  @connections = []
42
42
  @timeout = Server::DEFAULT_TIMEOUT
@@ -47,7 +47,7 @@ module Thin
47
47
  @ssl = nil
48
48
  @threaded = nil
49
49
  end
50
-
50
+
51
51
  # Start the backend and connect it.
52
52
  def start
53
53
  @stopping = false
@@ -55,7 +55,7 @@ module Thin
55
55
  connect
56
56
  @running = true
57
57
  end
58
-
58
+
59
59
  # Allow for early run up of eventmachine.
60
60
  if EventMachine.reactor_running?
61
61
  starter.call
@@ -63,67 +63,72 @@ module Thin
63
63
  EventMachine.run(&starter)
64
64
  end
65
65
  end
66
-
66
+
67
67
  # Stop of the backend from accepting new connections.
68
68
  def stop
69
69
  @running = false
70
70
  @stopping = true
71
-
71
+
72
72
  # Do not accept anymore connection
73
73
  disconnect
74
74
  stop! if @connections.empty?
75
75
  end
76
-
76
+
77
77
  # Force stop of the backend NOW, too bad for the current connections.
78
78
  def stop!
79
79
  @running = false
80
80
  @stopping = false
81
-
81
+
82
82
  EventMachine.stop if EventMachine.reactor_running?
83
83
  @connections.each { |connection| connection.close_connection }
84
84
  close
85
85
  end
86
-
86
+
87
87
  # Configure the backend. This method will be called before droping superuser privileges,
88
88
  # so you can do crazy stuff that require godlike powers here.
89
89
  def config
90
90
  # See http://rubyeventmachine.com/pub/rdoc/files/EPOLL.html
91
91
  EventMachine.epoll unless @no_epoll
92
-
92
+
93
93
  # Set the maximum number of socket descriptors that the server may open.
94
94
  # The process needs to have required privilege to set it higher the 1024 on
95
95
  # some systems.
96
96
  @maximum_connections = EventMachine.set_descriptor_table_size(@maximum_connections) unless Thin.win?
97
97
  end
98
-
98
+
99
99
  # Free up resources used by the backend.
100
100
  def close
101
101
  end
102
-
102
+
103
103
  # Returns +true+ if the backend is connected and running.
104
104
  def running?
105
105
  @running
106
106
  end
107
-
107
+
108
108
  # Called by a connection when it's unbinded.
109
109
  def connection_finished(connection)
110
110
  @persistent_connection_count -= 1 if connection.can_persist?
111
111
  @connections.delete(connection)
112
-
112
+
113
113
  # Finalize gracefull stop if there's no more active connection.
114
114
  stop! if @stopping && @connections.empty?
115
115
  end
116
-
116
+
117
117
  # Returns +true+ if no active connection.
118
118
  def empty?
119
119
  @connections.empty?
120
120
  end
121
-
121
+
122
122
  # Number of active connections.
123
123
  def size
124
124
  @connections.size
125
125
  end
126
-
126
+
127
+ # connection list.
128
+ def connections_list
129
+ @connections
130
+ end
131
+
127
132
  protected
128
133
  # Initialize a new connection to a client.
129
134
  def initialize_connection(connection)
@@ -131,7 +136,7 @@ module Thin
131
136
  connection.app = @server.app
132
137
  connection.comm_inactivity_timeout = @timeout
133
138
  connection.threaded = @threaded
134
-
139
+
135
140
  if @ssl
136
141
  connection.start_tls(@ssl_options)
137
142
  end
@@ -145,7 +150,7 @@ module Thin
145
150
 
146
151
  @connections << connection
147
152
  end
148
-
153
+
149
154
  end
150
155
  end
151
156
  end
@@ -3,7 +3,7 @@ require 'socket'
3
3
  module Thin
4
4
  # An exception class to handle the event that server didn't start on time
5
5
  class RestartTimeout < RuntimeError; end
6
-
6
+
7
7
  module Controllers
8
8
  # Control a set of servers.
9
9
  # * Generate start and stop commands and run them.
@@ -13,17 +13,17 @@ module Thin
13
13
  # Cluster only options that should not be passed in the command sent
14
14
  # to the indiviual servers.
15
15
  CLUSTER_OPTIONS = [:servers, :only, :onebyone, :wait]
16
-
16
+
17
17
  # Maximum wait time for the server to be restarted
18
18
  DEFAULT_WAIT_TIME = 30 # seconds
19
-
19
+
20
20
  # Create a new cluster of servers launched using +options+.
21
21
  def initialize(options)
22
22
  super
23
23
  # Cluster can only contain daemonized servers
24
24
  @options.merge!(:daemonize => true)
25
25
  end
26
-
26
+
27
27
  def first_port; @options[:port] end
28
28
  def address; @options[:address] end
29
29
  def socket; @options[:socket] end
@@ -33,35 +33,35 @@ module Thin
33
33
  def only; @options[:only] end
34
34
  def onebyone; @options[:onebyone] end
35
35
  def wait; @options[:wait] end
36
-
36
+
37
37
  def swiftiply?
38
38
  @options.has_key?(:swiftiply)
39
39
  end
40
-
40
+
41
41
  # Start the servers
42
42
  def start
43
43
  with_each_server { |n| start_server n }
44
44
  end
45
-
45
+
46
46
  # Start a single server
47
47
  def start_server(number)
48
48
  log "Starting server on #{server_id(number)} ... "
49
-
49
+
50
50
  run :start, number
51
51
  end
52
-
52
+
53
53
  # Stop the servers
54
54
  def stop
55
55
  with_each_server { |n| stop_server n }
56
56
  end
57
-
57
+
58
58
  # Stop a single server
59
59
  def stop_server(number)
60
60
  log "Stopping server on #{server_id(number)} ... "
61
-
61
+
62
62
  run :stop, number
63
63
  end
64
-
64
+
65
65
  # Stop and start the servers.
66
66
  def restart
67
67
  unless onebyone
@@ -70,7 +70,7 @@ module Thin
70
70
  sleep 0.1 # Let's breath a bit shall we ?
71
71
  start
72
72
  else
73
- with_each_server do |n|
73
+ with_each_server do |n|
74
74
  stop_server(n)
75
75
  sleep 0.1 # Let's breath a bit shall we ?
76
76
  start_server(n)
@@ -78,7 +78,7 @@ module Thin
78
78
  end
79
79
  end
80
80
  end
81
-
81
+
82
82
  def test_socket(number)
83
83
  if socket
84
84
  UNIXSocket.new(socket_for(number))
@@ -88,12 +88,12 @@ module Thin
88
88
  rescue
89
89
  nil
90
90
  end
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
94
  log "Waiting for server to start ..."
95
95
  STDOUT.flush # Need this to make sure user got the message
96
-
96
+
97
97
  tries = 0
98
98
  loop do
99
99
  if test_socket = test_socket(number)
@@ -109,7 +109,7 @@ module Thin
109
109
  end
110
110
  end
111
111
  end
112
-
112
+
113
113
  def server_id(number)
114
114
  if socket
115
115
  socket_for(number)
@@ -119,23 +119,23 @@ module Thin
119
119
  [address, number].join(':')
120
120
  end
121
121
  end
122
-
122
+
123
123
  def log_file_for(number)
124
124
  include_server_number log_file, number
125
125
  end
126
-
126
+
127
127
  def pid_file_for(number)
128
128
  include_server_number pid_file, number
129
129
  end
130
-
130
+
131
131
  def socket_for(number)
132
132
  include_server_number socket, number
133
133
  end
134
-
134
+
135
135
  def pid_for(number)
136
136
  File.read(pid_file_for(number)).chomp.to_i
137
137
  end
138
-
138
+
139
139
  private
140
140
  # Send the command to the +thin+ script
141
141
  def run(cmd, number)
@@ -150,10 +150,10 @@ module Thin
150
150
  end
151
151
  Command.run(cmd, cmd_options)
152
152
  end
153
-
153
+
154
154
  def with_each_server
155
155
  if only
156
- if first_port && only < 80
156
+ if first_port && only < 1024
157
157
  # interpret +only+ as a sequence number
158
158
  yield first_port + only
159
159
  else
@@ -166,7 +166,7 @@ module Thin
166
166
  size.times { |n| yield first_port + n }
167
167
  end
168
168
  end
169
-
169
+
170
170
  # Add the server port or number in the filename
171
171
  # so each instance get its own file
172
172
  def include_server_number(path, number)
@@ -16,7 +16,7 @@ end
16
16
  module Thin
17
17
  # Raised when the pid file already exist starting as a daemon.
18
18
  class PidFileExist < RuntimeError; end
19
-
19
+
20
20
  # Module included in classes that can be turned into a daemon.
21
21
  # Handle stuff like:
22
22
  # * storing the PID in a file
@@ -25,37 +25,37 @@ module Thin
25
25
  # * killing the process gracefully
26
26
  module Daemonizable
27
27
  attr_accessor :pid_file, :log_file
28
-
28
+
29
29
  def self.included(base)
30
30
  base.extend ClassMethods
31
31
  end
32
-
32
+
33
33
  def pid
34
- File.exist?(pid_file) ? open(pid_file).read.to_i : nil
34
+ File.exist?(pid_file) && !File.zero?(pid_file) ? open(pid_file).read.to_i : nil
35
35
  end
36
-
36
+
37
37
  # Turns the current script into a daemon process that detaches from the console.
38
38
  def daemonize
39
39
  raise PlatformNotSupported, 'Daemonizing is not supported on Windows' if Thin.win?
40
40
  raise ArgumentError, 'You must specify a pid_file to daemonize' unless @pid_file
41
-
41
+
42
42
  remove_stale_pid_file
43
-
43
+
44
44
  pwd = Dir.pwd # Current directory is changed during daemonization, so store it
45
-
45
+
46
46
  # HACK we need to create the directory before daemonization to prevent a bug under 1.9
47
47
  # ignoring all signals when the directory is created after daemonization.
48
48
  FileUtils.mkdir_p File.dirname(@pid_file)
49
49
  FileUtils.mkdir_p File.dirname(@log_file)
50
-
50
+
51
51
  Daemonize.daemonize(File.expand_path(@log_file), name)
52
-
52
+
53
53
  Dir.chdir(pwd)
54
-
54
+
55
55
  write_pid_file
56
56
 
57
57
  self.after_daemonize if self.respond_to? :after_daemonize
58
-
58
+
59
59
  at_exit do
60
60
  log ">> Exiting!"
61
61
  remove_pid_file
@@ -66,7 +66,7 @@ module Thin
66
66
  # to the specified user and group.
67
67
  def change_privilege(user, group=user)
68
68
  log ">> Changing process privilege to #{user}:#{group}"
69
-
69
+
70
70
  uid, gid = Process.euid, Process.egid
71
71
  target_uid = Etc.getpwnam(user).uid
72
72
  target_gid = Etc.getgrnam(group).gid
@@ -83,12 +83,12 @@ module Thin
83
83
  rescue Errno::EPERM => e
84
84
  log "Couldn't change user and group to #{user}:#{group}: #{e}"
85
85
  end
86
-
86
+
87
87
  # Register a proc to be called to restart the server.
88
88
  def on_restart(&block)
89
89
  @on_restart = block
90
90
  end
91
-
91
+
92
92
  # Restart the server.
93
93
  def restart
94
94
  if @on_restart
@@ -99,7 +99,7 @@ module Thin
99
99
  exit!
100
100
  end
101
101
  end
102
-
102
+
103
103
  module ClassMethods
104
104
  # Send a QUIT or INT (if timeout is +0+) signal the process which
105
105
  # PID is stored in +pid_file+.
@@ -112,12 +112,12 @@ module Thin
112
112
  send_signal('QUIT', pid_file, timeout)
113
113
  end
114
114
  end
115
-
115
+
116
116
  # Restart the server by sending HUP signal.
117
117
  def restart(pid_file)
118
118
  send_signal('HUP', pid_file)
119
119
  end
120
-
120
+
121
121
  # Send a +signal+ to the process which PID is stored in +pid_file+.
122
122
  def send_signal(signal, pid_file, timeout=60)
123
123
  if pid = read_pid_file(pid_file)
@@ -138,7 +138,7 @@ module Thin
138
138
  Logging.log "process not found!"
139
139
  force_kill pid_file
140
140
  end
141
-
141
+
142
142
  def force_kill(pid_file)
143
143
  if pid = read_pid_file(pid_file)
144
144
  Logging.log "Sending KILL signal to process #{pid} ... "
@@ -148,7 +148,7 @@ module Thin
148
148
  Logging.log "Can't stop process, no PID found in #{pid_file}"
149
149
  end
150
150
  end
151
-
151
+
152
152
  def read_pid_file(file)
153
153
  if File.file?(file) && pid = File.read(file)
154
154
  pid.to_i
@@ -157,18 +157,18 @@ module Thin
157
157
  end
158
158
  end
159
159
  end
160
-
160
+
161
161
  protected
162
162
  def remove_pid_file
163
163
  File.delete(@pid_file) if @pid_file && File.exists?(@pid_file)
164
164
  end
165
-
165
+
166
166
  def write_pid_file
167
167
  log ">> Writing PID to #{@pid_file}"
168
168
  open(@pid_file,"w") { |f| f.write(Process.pid) }
169
169
  File.chmod(0644, @pid_file)
170
170
  end
171
-
171
+
172
172
  # If PID file is stale, remove it.
173
173
  def remove_stale_pid_file
174
174
  if File.exist?(@pid_file)
data/lib/thin/server.rb CHANGED
@@ -12,7 +12,7 @@ module Thin
12
12
  # == UNIX domain server
13
13
  # Create a new UNIX domain socket bound to +socket+ file by specifiying a filename
14
14
  # as the first argument. Eg.: /tmp/thin.sock. If the first argument contains a <tt>/</tt>
15
- # it will be assumed to be a UNIX socket.
15
+ # it will be assumed to be a UNIX socket.
16
16
  #
17
17
  # Thin::Server.start('/tmp/thin.sock', app)
18
18
  #
@@ -30,7 +30,7 @@ module Thin
30
30
  # == Building an app in place
31
31
  # If a block is passed, a <tt>Rack::Builder</tt> instance
32
32
  # will be passed to build the +app+. So you can do cool stuff like this:
33
- #
33
+ #
34
34
  # Thin::Server.start('0.0.0.0', 3000) do
35
35
  # use Rack::CommonLogger
36
36
  # use Rack::ShowExceptions
@@ -44,19 +44,19 @@ module Thin
44
44
  # * QUIT: Gracefull shutdown (see Server#stop)
45
45
  # * INT and TERM: Force shutdown (see Server#stop!)
46
46
  # Disable signals by passing <tt>:signals => false</tt>
47
- #
47
+ #
48
48
  class Server
49
49
  include Logging
50
50
  include Daemonizable
51
51
  extend Forwardable
52
-
52
+
53
53
  # Default values
54
54
  DEFAULT_TIMEOUT = 30 #sec
55
55
  DEFAULT_HOST = '0.0.0.0'
56
56
  DEFAULT_PORT = 3000
57
57
  DEFAULT_MAXIMUM_CONNECTIONS = 1024
58
58
  DEFAULT_MAXIMUM_PERSISTENT_CONNECTIONS = 100
59
-
59
+
60
60
  # Application (Rack adapter) called with the request that produces the response.
61
61
  attr_accessor :app
62
62
 
@@ -65,38 +65,38 @@ module Thin
65
65
 
66
66
  # Backend handling the connections to the clients.
67
67
  attr_accessor :backend
68
-
68
+
69
69
  # Maximum number of seconds for incoming data to arrive before the connection
70
70
  # is dropped.
71
71
  def_delegators :backend, :timeout, :timeout=
72
-
72
+
73
73
  # Maximum number of file or socket descriptors that the server may open.
74
74
  def_delegators :backend, :maximum_connections, :maximum_connections=
75
-
75
+
76
76
  # Maximum number of connection that can be persistent at the same time.
77
77
  # Most browser never close the connection so most of the time they are closed
78
78
  # when the timeout occur. If we don't control the number of persistent connection,
79
79
  # if would be very easy to overflow the server for a DoS attack.
80
80
  def_delegators :backend, :maximum_persistent_connections, :maximum_persistent_connections=
81
-
81
+
82
82
  # Allow using threads in the backend.
83
83
  def_delegators :backend, :threaded?, :threaded=
84
-
84
+
85
85
  # Allow using SSL in the backend.
86
86
  def_delegators :backend, :ssl?, :ssl=, :ssl_options=
87
-
87
+
88
88
  # Address and port on which the server is listening for connections.
89
89
  def_delegators :backend, :host, :port
90
-
90
+
91
91
  # UNIX domain socket on which the server is listening for connections.
92
92
  def_delegator :backend, :socket
93
-
93
+
94
94
  # Disable the use of epoll under Linux
95
95
  def_delegators :backend, :no_epoll, :no_epoll=
96
-
96
+
97
97
  def initialize(*args, &block)
98
98
  host, port, options = DEFAULT_HOST, DEFAULT_PORT, {}
99
-
99
+
100
100
  # Guess each parameter by its type so they can be
101
101
  # received in any order.
102
102
  args.each do |arg|
@@ -108,58 +108,58 @@ module Thin
108
108
  @app = arg if arg.respond_to?(:call)
109
109
  end
110
110
  end
111
-
111
+
112
112
  # Set tag if needed
113
113
  self.tag = options[:tag]
114
114
 
115
115
  # Try to intelligently select which backend to use.
116
116
  @backend = select_backend(host, port, options)
117
-
117
+
118
118
  load_cgi_multipart_eof_fix
119
-
119
+
120
120
  @backend.server = self
121
-
121
+
122
122
  # Set defaults
123
123
  @backend.maximum_connections = DEFAULT_MAXIMUM_CONNECTIONS
124
124
  @backend.maximum_persistent_connections = DEFAULT_MAXIMUM_PERSISTENT_CONNECTIONS
125
125
  @backend.timeout = DEFAULT_TIMEOUT
126
-
126
+
127
127
  # Allow using Rack builder as a block
128
128
  @app = Rack::Builder.new(&block).to_app if block
129
-
129
+
130
130
  # If in debug mode, wrap in logger adapter
131
131
  @app = Rack::CommonLogger.new(@app) if Logging.debug?
132
-
132
+
133
133
  setup_signals unless options[:signals].class == FalseClass
134
134
  end
135
-
135
+
136
136
  # Lil' shortcut to turn this:
137
- #
137
+ #
138
138
  # Server.new(...).start
139
- #
139
+ #
140
140
  # into this:
141
- #
141
+ #
142
142
  # Server.start(...)
143
- #
143
+ #
144
144
  def self.start(*args, &block)
145
145
  new(*args, &block).start!
146
146
  end
147
-
147
+
148
148
  # Start the server and listen for connections.
149
149
  def start
150
150
  raise ArgumentError, 'app required' unless @app
151
-
151
+
152
152
  log ">> Thin web server (v#{VERSION::STRING} codename #{VERSION::CODENAME})"
153
153
  debug ">> Debugging ON"
154
154
  trace ">> Tracing ON"
155
-
155
+
156
156
  log ">> Maximum connections set to #{@backend.maximum_connections}"
157
157
  log ">> Listening on #{@backend}, CTRL+C to stop"
158
-
158
+
159
159
  @backend.start
160
160
  end
161
161
  alias :start! :start
162
-
162
+
163
163
  # == Gracefull shutdown
164
164
  # Stops the server after processing all current connections.
165
165
  # As soon as this method is called, the server stops accepting
@@ -176,7 +176,7 @@ module Thin
176
176
  stop!
177
177
  end
178
178
  end
179
-
179
+
180
180
  # == Force shutdown
181
181
  # Stops the server closing all current connections right away.
182
182
  # This doesn't wait for connection to finish their work and send data.
@@ -186,7 +186,7 @@ module Thin
186
186
 
187
187
  @backend.stop!
188
188
  end
189
-
189
+
190
190
  # == Reopen log file.
191
191
  # Reopen the log file and redirect STDOUT and STDERR to it.
192
192
  def reopen_log
@@ -195,39 +195,47 @@ module Thin
195
195
  log ">> Reopening log file: #{file}"
196
196
  Daemonize.redirect_io(file)
197
197
  end
198
-
198
+
199
199
  # == Configure the server
200
200
  # The process might need to have superuser privilege to configure
201
201
  # server with optimal options.
202
202
  def config
203
203
  @backend.config
204
204
  end
205
-
205
+
206
206
  # Name of the server and type of backend used.
207
207
  # This is also the name of the process in which Thin is running as a daemon.
208
208
  def name
209
209
  "thin server (#{@backend})" + (tag ? " [#{tag}]" : "")
210
210
  end
211
211
  alias :to_s :name
212
-
212
+
213
213
  # Return +true+ if the server is running and ready to receive requests.
214
214
  # Note that the server might still be running and return +false+ when
215
215
  # shuting down and waiting for active connections to complete.
216
216
  def running?
217
217
  @backend.running?
218
218
  end
219
-
219
+
220
220
  # deamonizing kills our HUP signal, so we set them again
221
221
  def after_daemonize
222
222
  setup_signals
223
223
  end
224
224
 
225
+ def print_status
226
+ puts "THIN_STATUS - '#{DateTime.now.to_s}' - '#{@backend.size}' - '#{@backend}' "
227
+ @backend.connections_list.each do |c|
228
+ puts "THIN_CONNECTION: #{c.request.env['REQUEST_METHOD']} #{c.request.env['REQUEST_URI']}" unless c.request.env['REQUEST_METHOD'].nil? || c.request.env['REQUEST_URI'].nil?
229
+ end
230
+ end
231
+
225
232
  protected
226
233
  # Register signals:
227
234
  # * TERM & QUIT calls +stop+ to shutdown gracefully.
228
235
  # * INT calls <tt>stop!</tt> to force shutdown.
229
236
  # * HUP calls <tt>restart</tt> to ... surprise, restart!
230
237
  # * USR1 reopen log files.
238
+ # * USR2 print status.
231
239
  def setup_signals
232
240
  trap('INT') { stop! }
233
241
  trap('TERM') { stop }
@@ -235,9 +243,10 @@ module Thin
235
243
  trap('QUIT') { stop }
236
244
  trap('HUP') { restart }
237
245
  trap('USR1') { reopen_log }
246
+ trap('USR2') { print_status }
238
247
  end
239
248
  end
240
-
249
+
241
250
  def select_backend(host, port, options)
242
251
  case
243
252
  when options.has_key?(:backend)
@@ -251,12 +260,12 @@ module Thin
251
260
  Backends::TcpServer.new(host, port)
252
261
  end
253
262
  end
254
-
263
+
255
264
  # Taken from Mongrel cgi_multipart_eof_fix
256
265
  # Ruby 1.8.5 has a security bug in cgi.rb, we need to patch it.
257
266
  def load_cgi_multipart_eof_fix
258
267
  version = RUBY_VERSION.split('.').map { |i| i.to_i }
259
-
268
+
260
269
  if version[0] <= 1 && version[1] <= 8 && version[2] <= 5 && RUBY_PLATFORM !~ /java/
261
270
  begin
262
271
  require 'cgi_multipart_eof_fix'
data/lib/thin/version.rb CHANGED
@@ -7,8 +7,9 @@ module Thin
7
7
  MAJOR = 1
8
8
  MINOR = 4
9
9
  TINY = 1
10
+ PATCH = 1
10
11
 
11
- STRING = [MAJOR, MINOR, TINY].join('.')
12
+ STRING = [MAJOR, MINOR, TINY, PATCH].join('.')
12
13
 
13
14
  CODENAME = "Chromeo".freeze
14
15
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: friendlyfashion-thin
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.4.1
4
+ version: 1.4.1.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -12,7 +12,7 @@ authors:
12
12
  autorequire:
13
13
  bindir: bin
14
14
  cert_chain: []
15
- date: 2012-09-05 00:00:00.000000000 Z
15
+ date: 2012-10-24 00:00:00.000000000 Z
16
16
  dependencies:
17
17
  - !ruby/object:Gem::Dependency
18
18
  name: rack
@@ -122,7 +122,7 @@ files:
122
122
  - ext/thin_parser/extconf.rb
123
123
  - ext/thin_parser/parser.rl
124
124
  - ext/thin_parser/common.rl
125
- homepage: http://code.macournoyer.com/thin/
125
+ homepage: https://github.com/friendlyfashion/thin
126
126
  licenses:
127
127
  - Ruby
128
128
  post_install_message: