thin 1.4.1 → 1.5.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.
- 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
|