thin 1.4.1 → 1.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +7 -0
- data/README.md +5 -24
- data/Rakefile +4 -1
- data/ext/thin_parser/thin.c +3 -3
- data/lib/thin/backends/base.rb +2 -0
- data/lib/thin/connection.rb +14 -4
- data/lib/thin/daemonizing.rb +5 -1
- data/lib/thin/headers.rb +6 -5
- data/lib/thin/request.rb +1 -1
- data/lib/thin/response.rb +5 -0
- data/lib/thin/runner.rb +2 -0
- data/lib/thin/server.rb +1 -1
- data/lib/thin/version.rb +3 -3
- metadata +2 -2
data/CHANGELOG
CHANGED
@@ -1,3 +1,10 @@
|
|
1
|
+
== 1.5.0 Knife
|
2
|
+
* Fix compilation under Ubuntu 12.04 with -Werror=format-security option.
|
3
|
+
* Raise an error when no PID file.
|
4
|
+
* Prevent duplicate response headers.
|
5
|
+
* Make proper response on exception [MasterLambaster].
|
6
|
+
* Automatically close idling pipeline connections on server stop [MasterLambaster].
|
7
|
+
|
1
8
|
== 1.4.1 Chromeo Fix
|
2
9
|
* Fix error when sending USR1 signal and no log file is supplied.
|
3
10
|
|
data/README.md
CHANGED
@@ -42,28 +42,10 @@ cd to/your/app
|
|
42
42
|
thin start
|
43
43
|
```
|
44
44
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
```ruby
|
49
|
-
#cat config.ru
|
50
|
-
app = proc do |env|
|
51
|
-
[
|
52
|
-
200,
|
53
|
-
{
|
54
|
-
'Content-Type' => 'text/html',
|
55
|
-
'Content-Length' => '2'
|
56
|
-
},
|
57
|
-
['hi']
|
58
|
-
]
|
59
|
-
end
|
60
|
-
|
61
|
-
run app
|
62
|
-
```
|
63
|
-
|
64
|
-
` thin start -R config.ru `
|
45
|
+
When using with Rails and Bundler, make sure to add `gem 'thin'`
|
46
|
+
to your Gemfile.
|
65
47
|
|
66
|
-
See example directory for
|
48
|
+
See example directory for samples and run 'thin -h' for usage.
|
67
49
|
|
68
50
|
License
|
69
51
|
=======
|
@@ -72,9 +54,8 @@ Ruby License, http://www.ruby-lang.org/en/LICENSE.txt.
|
|
72
54
|
Credits
|
73
55
|
=======
|
74
56
|
The parser was stolen from Mongrel http://mongrel.rubyforge.org by Zed Shaw.
|
75
|
-
Mongrel
|
76
|
-
|
77
|
-
either the terms of the GPL.
|
57
|
+
Mongrel is copyright 2007 Zed A. Shaw and contributors. It is licensed under
|
58
|
+
the Ruby license and the GPL2.
|
78
59
|
|
79
60
|
Thin is copyright Marc-Andre Cournoyer <macournoyer@gmail.com>
|
80
61
|
|
data/Rakefile
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
require 'rake'
|
2
|
-
require 'rake/clean'
|
3
2
|
load 'thin.gemspec'
|
4
3
|
|
5
4
|
# Load tasks in tasks/
|
@@ -17,5 +16,9 @@ task :push => :build do
|
|
17
16
|
sh "gem push thin-*.gem"
|
18
17
|
end
|
19
18
|
|
19
|
+
task :install => :build do
|
20
|
+
sh "gem install thin-*.gem"
|
21
|
+
end
|
22
|
+
|
20
23
|
desc "Release version #{Thin::VERSION::STRING}"
|
21
24
|
task :release => [:tag, :push]
|
data/ext/thin_parser/thin.c
CHANGED
@@ -47,7 +47,7 @@ static VALUE global_path_info;
|
|
47
47
|
#define DEF_MAX_LENGTH(N,length) const size_t MAX_##N##_LENGTH = length; const char *MAX_##N##_LENGTH_ERR = "HTTP element " # N " is longer than the " # length " allowed length."
|
48
48
|
|
49
49
|
/** Validates the max length of given input and throws an HttpParserError exception if over. */
|
50
|
-
#define VALIDATE_MAX_LENGTH(len, N) if(len > MAX_##N##_LENGTH) { rb_raise(eHttpParserError, MAX_##N##_LENGTH_ERR); }
|
50
|
+
#define VALIDATE_MAX_LENGTH(len, N) if(len > MAX_##N##_LENGTH) { rb_raise(eHttpParserError, "%s", MAX_##N##_LENGTH_ERR); }
|
51
51
|
|
52
52
|
/** Defines global strings in the init method. */
|
53
53
|
#define DEF_GLOBAL(N, val) global_##N = rb_obj_freeze(rb_str_new2(val)); rb_global_variable(&global_##N)
|
@@ -329,7 +329,7 @@ VALUE Thin_HttpParser_execute(VALUE self, VALUE req_hash, VALUE data, VALUE star
|
|
329
329
|
dlen = RSTRING_LEN(data);
|
330
330
|
|
331
331
|
if(from >= dlen) {
|
332
|
-
rb_raise(eHttpParserError, "Requested start is after data buffer end.");
|
332
|
+
rb_raise(eHttpParserError, "%s", "Requested start is after data buffer end.");
|
333
333
|
} else {
|
334
334
|
http->data = (void *)req_hash;
|
335
335
|
thin_http_parser_execute(http, dptr, dlen, from);
|
@@ -337,7 +337,7 @@ VALUE Thin_HttpParser_execute(VALUE self, VALUE req_hash, VALUE data, VALUE star
|
|
337
337
|
VALIDATE_MAX_LENGTH(http_parser_nread(http), HEADER);
|
338
338
|
|
339
339
|
if(thin_http_parser_has_error(http)) {
|
340
|
-
rb_raise(eHttpParserError, "Invalid HTTP format, parsing fails.");
|
340
|
+
rb_raise(eHttpParserError, "%s", "Invalid HTTP format, parsing fails.");
|
341
341
|
} else {
|
342
342
|
return INT2FIX(http_parser_nread(http));
|
343
343
|
}
|
data/lib/thin/backends/base.rb
CHANGED
data/lib/thin/connection.rb
CHANGED
@@ -34,12 +34,13 @@ module Thin
|
|
34
34
|
|
35
35
|
# Called when data is received from the client.
|
36
36
|
def receive_data(data)
|
37
|
+
@idle = false
|
37
38
|
trace { data }
|
38
39
|
process if @request.parse(data)
|
39
40
|
rescue InvalidRequest => e
|
40
41
|
log "!! Invalid request"
|
41
42
|
log_error e
|
42
|
-
|
43
|
+
post_process Response::BAD_REQUEST
|
43
44
|
end
|
44
45
|
|
45
46
|
# Called when all data was received and the request
|
@@ -82,8 +83,8 @@ module Thin
|
|
82
83
|
response
|
83
84
|
rescue Exception
|
84
85
|
handle_error
|
85
|
-
|
86
|
-
|
86
|
+
# Pass through error response
|
87
|
+
can_persist? && @request.persistent? ? Response::PERSISTENT_ERROR : Response::ERROR
|
87
88
|
end
|
88
89
|
|
89
90
|
def post_process(result)
|
@@ -108,6 +109,8 @@ module Thin
|
|
108
109
|
|
109
110
|
rescue Exception
|
110
111
|
handle_error
|
112
|
+
# Close connection since we can't handle response gracefully
|
113
|
+
close_connection
|
111
114
|
ensure
|
112
115
|
# If the body is being deferred, then terminate afterward.
|
113
116
|
if @response.body.respond_to?(:callback) && @response.body.respond_to?(:errback)
|
@@ -123,7 +126,6 @@ module Thin
|
|
123
126
|
def handle_error
|
124
127
|
log "!! Unexpected error while processing request: #{$!.message}"
|
125
128
|
log_error
|
126
|
-
close_connection rescue nil
|
127
129
|
end
|
128
130
|
|
129
131
|
def close_request_response
|
@@ -142,6 +144,8 @@ module Thin
|
|
142
144
|
close_request_response
|
143
145
|
else
|
144
146
|
close_request_response
|
147
|
+
# Connection become idle but it's still open
|
148
|
+
@idle = true
|
145
149
|
# Prepare the connection for another request if the client
|
146
150
|
# supports HTTP pipelining (persistent connection).
|
147
151
|
post_init
|
@@ -172,6 +176,12 @@ module Thin
|
|
172
176
|
@can_persist && @response.persistent?
|
173
177
|
end
|
174
178
|
|
179
|
+
# Return +true+ if the connection is open but is not
|
180
|
+
# processing any user requests
|
181
|
+
def idle?
|
182
|
+
@idle
|
183
|
+
end
|
184
|
+
|
175
185
|
# +true+ if <tt>app.call</tt> will be called inside a thread.
|
176
186
|
# You can set all requests as threaded setting <tt>Connection#threaded=true</tt>
|
177
187
|
# or on a per-request case returning +true+ in <tt>app.deferred?</tt>.
|
data/lib/thin/daemonizing.rb
CHANGED
@@ -16,6 +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
|
+
class PidFileNotFound < RuntimeError; end
|
19
20
|
|
20
21
|
# Module included in classes that can be turned into a daemon.
|
21
22
|
# Handle stuff like:
|
@@ -72,6 +73,9 @@ module Thin
|
|
72
73
|
target_gid = Etc.getgrnam(group).gid
|
73
74
|
|
74
75
|
if uid != target_uid || gid != target_gid
|
76
|
+
# Change PID file ownership
|
77
|
+
File.chown(target_uid, target_gid, @pid_file)
|
78
|
+
|
75
79
|
# Change process ownership
|
76
80
|
Process.initgroups(user, target_gid)
|
77
81
|
Process::GID.change_privilege(target_gid)
|
@@ -124,7 +128,7 @@ module Thin
|
|
124
128
|
sleep 0.1 while Process.running?(pid)
|
125
129
|
end
|
126
130
|
else
|
127
|
-
|
131
|
+
raise PidFileNotFound, "Can't stop process, no PID found in #{pid_file}"
|
128
132
|
end
|
129
133
|
rescue Timeout::Error
|
130
134
|
Logging.log "Timeout!"
|
data/lib/thin/headers.rb
CHANGED
@@ -3,7 +3,7 @@ module Thin
|
|
3
3
|
# and allow duplicated entries on some names.
|
4
4
|
class Headers
|
5
5
|
HEADER_FORMAT = "%s: %s\r\n".freeze
|
6
|
-
ALLOWED_DUPLICATES = %w(
|
6
|
+
ALLOWED_DUPLICATES = %w(set-cookie set-cookie2 warning www-authenticate).freeze
|
7
7
|
|
8
8
|
def initialize
|
9
9
|
@sent = {}
|
@@ -14,8 +14,9 @@ module Thin
|
|
14
14
|
# Ignore if already sent and no duplicates are allowed
|
15
15
|
# for this +key+.
|
16
16
|
def []=(key, value)
|
17
|
-
|
18
|
-
|
17
|
+
downcase_key = key.downcase
|
18
|
+
if !@sent.has_key?(downcase_key) || ALLOWED_DUPLICATES.include?(downcase_key)
|
19
|
+
@sent[downcase_key] = true
|
19
20
|
value = case value
|
20
21
|
when Time
|
21
22
|
value.httpdate
|
@@ -29,11 +30,11 @@ module Thin
|
|
29
30
|
end
|
30
31
|
|
31
32
|
def has_key?(key)
|
32
|
-
@sent[key]
|
33
|
+
@sent[key.downcase]
|
33
34
|
end
|
34
35
|
|
35
36
|
def to_s
|
36
37
|
@out.join
|
37
38
|
end
|
38
39
|
end
|
39
|
-
end
|
40
|
+
end
|
data/lib/thin/request.rb
CHANGED
@@ -74,7 +74,7 @@ module Thin
|
|
74
74
|
# Returns +true+ if the parsing is complete.
|
75
75
|
def parse(data)
|
76
76
|
if @parser.finished? # Header finished, can only be some more body
|
77
|
-
body << data
|
77
|
+
@body << data
|
78
78
|
else # Parse more header using the super parser
|
79
79
|
@data << data
|
80
80
|
raise InvalidRequest, 'Header longer than allowed' if @data.size > MAX_HEADER
|
data/lib/thin/response.rb
CHANGED
@@ -9,6 +9,11 @@ module Thin
|
|
9
9
|
|
10
10
|
PERSISTENT_STATUSES = [100, 101].freeze
|
11
11
|
|
12
|
+
#Error Responses
|
13
|
+
ERROR = [500, {'Content-Type' => 'text/plain'}, ['Internal server error']].freeze
|
14
|
+
PERSISTENT_ERROR = [500, {'Content-Type' => 'text/plain', 'Connection' => 'keep-alive', 'Content-Length' => "21"}, ['Internal server error']].freeze
|
15
|
+
BAD_REQUEST = [400, {'Content-Type' => 'text/plain'}, ['Bad Request']].freeze
|
16
|
+
|
12
17
|
# Status code
|
13
18
|
attr_accessor :status
|
14
19
|
|
data/lib/thin/runner.rb
CHANGED
@@ -130,6 +130,7 @@ module Thin
|
|
130
130
|
opts.separator "Common options:"
|
131
131
|
|
132
132
|
opts.on_tail("-r", "--require FILE", "require the library") { |file| @options[:require] << file }
|
133
|
+
opts.on_tail("-q", "--quiet", "Silence all logging") { @options[:quiet] = true }
|
133
134
|
opts.on_tail("-D", "--debug", "Set debugging on") { @options[:debug] = true }
|
134
135
|
opts.on_tail("-V", "--trace", "Set tracing on (log raw request/response)") { @options[:trace] = true }
|
135
136
|
opts.on_tail("-h", "--help", "Show this message") { puts opts; exit }
|
@@ -173,6 +174,7 @@ module Thin
|
|
173
174
|
@options[:require].each { |r| ruby_require r }
|
174
175
|
Logging.debug = @options[:debug]
|
175
176
|
Logging.trace = @options[:trace]
|
177
|
+
Logging.silent = @options[:quiet]
|
176
178
|
|
177
179
|
controller = case
|
178
180
|
when cluster? then Controllers::Cluster.new(@options)
|
data/lib/thin/server.rb
CHANGED
@@ -55,7 +55,7 @@ module Thin
|
|
55
55
|
DEFAULT_HOST = '0.0.0.0'
|
56
56
|
DEFAULT_PORT = 3000
|
57
57
|
DEFAULT_MAXIMUM_CONNECTIONS = 1024
|
58
|
-
DEFAULT_MAXIMUM_PERSISTENT_CONNECTIONS =
|
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
|
data/lib/thin/version.rb
CHANGED
@@ -5,12 +5,12 @@ module Thin
|
|
5
5
|
|
6
6
|
module VERSION #:nodoc:
|
7
7
|
MAJOR = 1
|
8
|
-
MINOR =
|
9
|
-
TINY =
|
8
|
+
MINOR = 5
|
9
|
+
TINY = 0
|
10
10
|
|
11
11
|
STRING = [MAJOR, MINOR, TINY].join('.')
|
12
12
|
|
13
|
-
CODENAME = "
|
13
|
+
CODENAME = "Knife".freeze
|
14
14
|
|
15
15
|
RACK = [1, 0].freeze # Rack protocol version
|
16
16
|
end
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: thin
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 1.
|
5
|
+
version: 1.5.0
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Marc-Andre Cournoyer
|
@@ -10,7 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2012-
|
13
|
+
date: 2012-09-22 00:00:00 -04:00
|
14
14
|
default_executable:
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|