unicorn 3.1.0 → 3.2.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +1 -0
- data/.gitignore +1 -0
- data/.wrongdoc.yml +8 -0
- data/GIT-VERSION-GEN +1 -1
- data/GNUmakefile +24 -64
- data/Rakefile +0 -103
- data/TODO +0 -2
- data/ext/unicorn_http/global_variables.h +4 -0
- data/ext/unicorn_http/unicorn_http.rl +145 -30
- data/lib/unicorn/configurator.rb +13 -2
- data/lib/unicorn/const.rb +2 -2
- data/lib/unicorn/http_response.rb +0 -2
- data/lib/unicorn/http_server.rb +17 -11
- data/script/isolate_for_tests +1 -1
- data/t/t0016-trust-x-forwarded-false.sh +30 -0
- data/t/t0017-trust-x-forwarded-true.sh +30 -0
- data/test/unit/test_http_parser.rb +55 -0
- data/test/unit/test_http_parser_ng.rb +85 -6
- data/test/unit/test_http_parser_xftrust.rb +38 -0
- data/unicorn.gemspec +12 -24
- metadata +34 -10
data/.document
CHANGED
data/.gitignore
CHANGED
data/.wrongdoc.yml
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
---
|
2
|
+
cgit_url: http://git.bogomips.org/cgit/unicorn.git
|
3
|
+
git_url: git://git.bogomips.org/unicorn.git
|
4
|
+
rdoc_url: http://unicorn.bogomips.org/
|
5
|
+
changelog_start: v1.1.5
|
6
|
+
merge_html:
|
7
|
+
unicorn_1: Documentation/unicorn.1.html
|
8
|
+
unicorn_rails_1: Documentation/unicorn_rails.1.html
|
data/GIT-VERSION-GEN
CHANGED
data/GNUmakefile
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
# use GNU Make to run tests in parallel, and without depending on RubyGems
|
2
2
|
all:: test
|
3
3
|
|
4
|
-
GIT_URL = git://git.bogomips.org/unicorn.git
|
5
4
|
RLFLAGS = -G2
|
6
5
|
|
7
6
|
MRI = ruby
|
@@ -156,77 +155,39 @@ clean:
|
|
156
155
|
$(RM) $(setup_rb_files) $(t_log)
|
157
156
|
$(RM) -r $(test_prefix) man
|
158
157
|
|
159
|
-
man:
|
160
|
-
$(MAKE) -C Documentation install
|
158
|
+
man html:
|
159
|
+
$(MAKE) -C Documentation install-$@
|
161
160
|
|
162
|
-
pkg_extra := GIT-VERSION-FILE NEWS
|
163
|
-
|
164
|
-
$(RM) .manifest
|
165
|
-
$(MAKE) .manifest
|
161
|
+
pkg_extra := GIT-VERSION-FILE ChangeLog LATEST NEWS \
|
162
|
+
$(ext)/unicorn_http.c $(man1_paths)
|
166
163
|
|
167
|
-
.
|
168
|
-
|
169
|
-
|
170
|
-
|
164
|
+
ChangeLog: GIT-VERSION-FILE .wrongdoc.yml
|
165
|
+
wrongdoc prepare
|
166
|
+
|
167
|
+
.manifest: ChangeLog $(ext)/unicorn_http.c
|
168
|
+
(git ls-files && for i in $@ $(pkg_extra); do echo $$i; done) | \
|
169
|
+
LC_ALL=C sort > $@+
|
171
170
|
cmp $@+ $@ || mv $@+ $@
|
172
171
|
$(RM) $@+
|
173
172
|
|
174
|
-
|
175
|
-
$(RAKE) -s news_rdoc > $@+
|
176
|
-
mv $@+ $@
|
177
|
-
|
178
|
-
SINCE = 1.1.5
|
179
|
-
ChangeLog: LOG_VERSION = \
|
180
|
-
$(shell git rev-parse -q "$(GIT_VERSION)" >/dev/null 2>&1 && \
|
181
|
-
echo $(GIT_VERSION) || git describe)
|
182
|
-
ChangeLog: log_range = v$(SINCE)..$(LOG_VERSION)
|
183
|
-
ChangeLog: GIT-VERSION-FILE
|
184
|
-
@echo "ChangeLog from $(GIT_URL) ($(log_range))" > $@+
|
185
|
-
@echo >> $@+
|
186
|
-
git log $(log_range) | sed -e 's/^/ /' >> $@+
|
187
|
-
mv $@+ $@
|
188
|
-
|
189
|
-
news_atom := http://unicorn.bogomips.org/NEWS.atom.xml
|
190
|
-
cgit_atom := http://git.bogomips.org/cgit/unicorn.git/atom/?h=master
|
191
|
-
atom = <link rel="alternate" title="Atom feed" href="$(1)" \
|
192
|
-
type="application/atom+xml"/>
|
193
|
-
|
194
|
-
# using rdoc 2.5.x+
|
195
|
-
doc: .document $(ext)/unicorn_http.c NEWS ChangeLog
|
173
|
+
doc: .document $(ext)/unicorn_http.c man html .wrongdoc.yml
|
196
174
|
for i in $(man1_rdoc); do echo > $$i; done
|
197
175
|
find bin lib -type f -name '*.rbc' -exec rm -f '{}' ';'
|
198
|
-
|
176
|
+
$(RM) -r doc
|
177
|
+
wrongdoc all
|
199
178
|
install -m644 COPYING doc/COPYING
|
200
|
-
install -m644 $(shell grep '^[A-Z]' .document)
|
201
|
-
$(MAKE) -C Documentation install-html install-man
|
179
|
+
install -m644 $(shell grep '^[A-Z]' .document) doc/
|
202
180
|
install -m644 $(man1_paths) doc/
|
203
|
-
cd doc &&
|
204
|
-
$(RM) 1.html $${i}.1.html; \
|
205
|
-
sed -e '/"documentation">/r man1/'$$i'.1.html' \
|
206
|
-
< $${i}_1.html > tmp && mv tmp $${i}_1.html; \
|
207
|
-
ln $${i}_1.html $${i}.1.html; \
|
208
|
-
done
|
209
|
-
$(RUBY) -i -p -e \
|
210
|
-
'$$_.gsub!("</title>",%q{\&$(call atom,$(cgit_atom))})' \
|
211
|
-
doc/ChangeLog.html
|
212
|
-
$(RUBY) -i -p -e \
|
213
|
-
'$$_.gsub!("</title>",%q{\&$(call atom,$(news_atom))})' \
|
214
|
-
doc/NEWS.html doc/README.html
|
215
|
-
$(RAKE) -s news_atom > doc/NEWS.atom.xml
|
216
|
-
cd doc && ln README.html tmp && mv tmp index.html
|
181
|
+
tar cf - $$(git ls-files examples/) | (cd doc && tar xf -)
|
217
182
|
$(RM) $(man1_rdoc)
|
218
183
|
|
219
184
|
# publishes docs to http://unicorn.bogomips.org
|
220
185
|
publish_doc:
|
221
186
|
-git set-file-times
|
222
|
-
$(
|
223
|
-
|
224
|
-
|
225
|
-
< NEWS > doc/LATEST
|
226
|
-
find doc/images doc/js -type f | \
|
227
|
-
TZ=UTC xargs touch -d '1970-01-01 00:00:00' doc/rdoc.css
|
187
|
+
$(MAKE) doc
|
188
|
+
find doc/images -type f | \
|
189
|
+
TZ=UTC xargs touch -d '1970-01-01 00:00:02' doc/rdoc.css
|
228
190
|
$(MAKE) doc_gz
|
229
|
-
tar cf - $$(git ls-files examples/) | (cd doc && tar xf -)
|
230
191
|
chmod 644 $$(find doc -type f)
|
231
192
|
$(RSYNC) -av doc/ unicorn.bogomips.org:/srv/unicorn/
|
232
193
|
git ls-files | xargs touch
|
@@ -235,7 +196,6 @@ publish_doc:
|
|
235
196
|
# "gzip_static on" can serve the gzipped versions directly.
|
236
197
|
doc_gz: docs = $(shell find doc -type f ! -regex '^.*\.\(gif\|jpg\|png\|gz\)$$')
|
237
198
|
doc_gz:
|
238
|
-
touch doc/NEWS.atom.xml -d "$$(awk 'NR==1{print $$4,$$5,$$6}' NEWS)"
|
239
199
|
for i in $(docs); do \
|
240
200
|
gzip --rsyncable -9 < $$i > $$i.gz; touch -r $$i $$i.gz; done
|
241
201
|
|
@@ -274,10 +234,10 @@ release_changes := release_changes-$(VERSION)
|
|
274
234
|
release-notes: $(release_notes)
|
275
235
|
release-changes: $(release_changes)
|
276
236
|
$(release_changes):
|
277
|
-
|
237
|
+
wrongdoc release_changes > $@+
|
278
238
|
$(VISUAL) $@+ && test -s $@+ && mv $@+ $@
|
279
239
|
$(release_notes):
|
280
|
-
|
240
|
+
wrongdoc release_notes > $@+
|
281
241
|
$(VISUAL) $@+ && test -s $@+ && mv $@+ $@
|
282
242
|
|
283
243
|
# ensures we're actually on the tagged $(VERSION), only used for release
|
@@ -297,18 +257,18 @@ gem: $(pkggem)
|
|
297
257
|
install-gem: $(pkggem)
|
298
258
|
gem install $(CURDIR)/$<
|
299
259
|
|
300
|
-
$(pkggem): manifest fix-perms
|
260
|
+
$(pkggem): .manifest fix-perms
|
301
261
|
gem build $(rfpackage).gemspec
|
302
262
|
mkdir -p pkg
|
303
263
|
mv $(@F) $@
|
304
264
|
|
305
265
|
$(pkgtgz): distdir = $(basename $@)
|
306
266
|
$(pkgtgz): HEAD = v$(VERSION)
|
307
|
-
$(pkgtgz): manifest fix-perms
|
267
|
+
$(pkgtgz): .manifest fix-perms
|
308
268
|
@test -n "$(distdir)"
|
309
269
|
$(RM) -r $(distdir)
|
310
270
|
mkdir -p $(distdir)
|
311
|
-
tar cf -
|
271
|
+
tar cf - $$(cat .manifest) | (cd $(distdir) && tar xf -)
|
312
272
|
cd pkg && tar cf - $(basename $(@F)) | gzip -9 > $(@F)+
|
313
273
|
mv $@+ $@
|
314
274
|
|
@@ -330,5 +290,5 @@ gem install-gem: GIT-VERSION-FILE
|
|
330
290
|
$(MAKE) $@ VERSION=$(GIT_VERSION)
|
331
291
|
endif
|
332
292
|
|
333
|
-
.PHONY: .FORCE-GIT-VERSION-FILE doc $(T) $(slow_tests)
|
293
|
+
.PHONY: .FORCE-GIT-VERSION-FILE doc $(T) $(slow_tests) man
|
334
294
|
.PHONY: test-install
|
data/Rakefile
CHANGED
@@ -1,112 +1,9 @@
|
|
1
1
|
# -*- encoding: binary -*-
|
2
2
|
autoload :Gem, 'rubygems'
|
3
3
|
|
4
|
-
# most tasks are in the GNUmakefile which offers better parallelism
|
5
|
-
|
6
|
-
def old_summaries
|
7
|
-
@old_summaries ||= File.readlines(".CHANGELOG.old").inject({}) do |hash, line|
|
8
|
-
version, summary = line.split(/ - /, 2)
|
9
|
-
hash[version] = summary
|
10
|
-
hash
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
def tags
|
15
|
-
timefmt = '%Y-%m-%dT%H:%M:%SZ'
|
16
|
-
@tags ||= `git tag -l`.split(/\n/).map do |tag|
|
17
|
-
next if tag == "v0.0.0"
|
18
|
-
if %r{\Av[\d\.]+} =~ tag
|
19
|
-
header, subject, body = `git cat-file tag #{tag}`.split(/\n\n/, 3)
|
20
|
-
header = header.split(/\n/)
|
21
|
-
tagger = header.grep(/\Atagger /).first
|
22
|
-
body ||= "initial"
|
23
|
-
{
|
24
|
-
:time => Time.at(tagger.split(/ /)[-2].to_i).utc.strftime(timefmt),
|
25
|
-
:tagger_name => %r{^tagger ([^<]+)}.match(tagger)[1].strip,
|
26
|
-
:tagger_email => %r{<([^>]+)>}.match(tagger)[1].strip,
|
27
|
-
:id => `git rev-parse refs/tags/#{tag}`.chomp!,
|
28
|
-
:tag => tag,
|
29
|
-
:subject => subject,
|
30
|
-
:body => (old = old_summaries[tag]) ? "#{old}\n#{body}" : body,
|
31
|
-
}
|
32
|
-
end
|
33
|
-
end.compact.sort { |a,b| b[:time] <=> a[:time] }
|
34
|
-
end
|
35
|
-
|
36
4
|
cgit_url = "http://git.bogomips.org/cgit/unicorn.git"
|
37
5
|
git_url = ENV['GIT_URL'] || 'git://git.bogomips.org/unicorn.git'
|
38
6
|
|
39
|
-
desc 'prints news as an Atom feed'
|
40
|
-
task :news_atom do
|
41
|
-
require 'nokogiri'
|
42
|
-
new_tags = tags[0,10]
|
43
|
-
puts(Nokogiri::XML::Builder.new do
|
44
|
-
feed :xmlns => "http://www.w3.org/2005/Atom" do
|
45
|
-
id! "http://unicorn.bogomips.org/NEWS.atom.xml"
|
46
|
-
title "Unicorn news"
|
47
|
-
subtitle "Rack HTTP server for Unix and fast clients"
|
48
|
-
link! :rel => 'alternate', :type => 'text/html',
|
49
|
-
:href => 'http://unicorn.bogomips.org/NEWS.html'
|
50
|
-
updated new_tags.first[:time]
|
51
|
-
new_tags.each do |tag|
|
52
|
-
entry do
|
53
|
-
title tag[:subject]
|
54
|
-
updated tag[:time]
|
55
|
-
published tag[:time]
|
56
|
-
author {
|
57
|
-
name tag[:tagger_name]
|
58
|
-
email tag[:tagger_email]
|
59
|
-
}
|
60
|
-
url = "#{cgit_url}/tag/?id=#{tag[:tag]}"
|
61
|
-
link! :rel => "alternate", :type => "text/html", :href =>url
|
62
|
-
id! url
|
63
|
-
message_only = tag[:body].split(/\n.+\(\d+\):\n {6}/s).first.strip
|
64
|
-
content({:type =>:text}, message_only)
|
65
|
-
content(:type =>:xhtml) { pre tag[:body] }
|
66
|
-
end
|
67
|
-
end
|
68
|
-
end
|
69
|
-
end.to_xml)
|
70
|
-
end
|
71
|
-
|
72
|
-
desc 'prints RDoc-formatted news'
|
73
|
-
task :news_rdoc do
|
74
|
-
tags.each do |tag|
|
75
|
-
time = tag[:time].tr!('T', ' ').gsub!(/:\d\dZ/, ' UTC')
|
76
|
-
puts "=== #{tag[:tag].sub(/^v/, '')} / #{time}"
|
77
|
-
puts ""
|
78
|
-
|
79
|
-
body = tag[:body]
|
80
|
-
puts tag[:body].gsub(/^/sm, " ").gsub(/[ \t]+$/sm, "")
|
81
|
-
puts ""
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
|
-
desc "print release changelog for Rubyforge"
|
86
|
-
task :release_changes do
|
87
|
-
version = ENV['VERSION'] or abort "VERSION= needed"
|
88
|
-
version = "v#{version}"
|
89
|
-
vtags = tags.map { |tag| tag[:tag] =~ /\Av/ and tag[:tag] }.sort
|
90
|
-
prev = vtags[vtags.index(version) - 1]
|
91
|
-
system('git', 'diff', '--stat', prev, version) or abort $?
|
92
|
-
puts ""
|
93
|
-
system('git', 'log', "#{prev}..#{version}") or abort $?
|
94
|
-
end
|
95
|
-
|
96
|
-
desc "print release notes for Rubyforge"
|
97
|
-
task :release_notes do
|
98
|
-
spec = Gem::Specification.load('unicorn.gemspec')
|
99
|
-
puts spec.description.strip
|
100
|
-
puts ""
|
101
|
-
puts "* #{spec.homepage}"
|
102
|
-
puts "* #{spec.email}"
|
103
|
-
puts "* #{git_url}"
|
104
|
-
|
105
|
-
_, _, body = `git cat-file tag v#{spec.version}`.split(/\n\n/, 3)
|
106
|
-
print "\nChanges:\n\n"
|
107
|
-
puts body
|
108
|
-
end
|
109
|
-
|
110
7
|
desc "post to RAA"
|
111
8
|
task :raa_update do
|
112
9
|
require 'net/http'
|
data/TODO
CHANGED
@@ -15,6 +15,7 @@ static VALUE g_server_port;
|
|
15
15
|
static VALUE g_server_protocol;
|
16
16
|
static VALUE g_http_host;
|
17
17
|
static VALUE g_http_x_forwarded_proto;
|
18
|
+
static VALUE g_http_x_forwarded_ssl;
|
18
19
|
static VALUE g_http_transfer_encoding;
|
19
20
|
static VALUE g_content_length;
|
20
21
|
static VALUE g_http_trailer;
|
@@ -23,6 +24,7 @@ static VALUE g_port_80;
|
|
23
24
|
static VALUE g_port_443;
|
24
25
|
static VALUE g_localhost;
|
25
26
|
static VALUE g_http;
|
27
|
+
static VALUE g_https;
|
26
28
|
static VALUE g_http_09;
|
27
29
|
static VALUE g_http_10;
|
28
30
|
static VALUE g_http_11;
|
@@ -73,10 +75,12 @@ static void init_globals(void)
|
|
73
75
|
DEF_GLOBAL(server_port, "SERVER_PORT");
|
74
76
|
DEF_GLOBAL(server_protocol, "SERVER_PROTOCOL");
|
75
77
|
DEF_GLOBAL(http_x_forwarded_proto, "HTTP_X_FORWARDED_PROTO");
|
78
|
+
DEF_GLOBAL(http_x_forwarded_ssl, "HTTP_X_FORWARDED_SSL");
|
76
79
|
DEF_GLOBAL(port_80, "80");
|
77
80
|
DEF_GLOBAL(port_443, "443");
|
78
81
|
DEF_GLOBAL(localhost, "localhost");
|
79
82
|
DEF_GLOBAL(http, "http");
|
83
|
+
DEF_GLOBAL(https, "https");
|
80
84
|
DEF_GLOBAL(http_11, "HTTP/1.1");
|
81
85
|
DEF_GLOBAL(http_10, "HTTP/1.0");
|
82
86
|
DEF_GLOBAL(http_09, "HTTP/0.9");
|
@@ -21,14 +21,70 @@
|
|
21
21
|
#define UH_FL_REQEOF 0x40
|
22
22
|
#define UH_FL_KAVERSION 0x80
|
23
23
|
#define UH_FL_HASHEADER 0x100
|
24
|
+
#define UH_FL_TO_CLEAR 0x200
|
24
25
|
|
25
26
|
/* all of these flags need to be set for keepalive to be supported */
|
26
27
|
#define UH_FL_KEEPALIVE (UH_FL_KAVERSION | UH_FL_REQEOF | UH_FL_HASHEADER)
|
27
28
|
|
29
|
+
/*
|
30
|
+
* whether or not to trust X-Forwarded-Proto and X-Forwarded-SSL when
|
31
|
+
* setting rack.url_scheme
|
32
|
+
*/
|
33
|
+
static VALUE trust_x_forward = Qtrue;
|
34
|
+
|
35
|
+
static unsigned long keepalive_requests = 100; /* same as nginx */
|
36
|
+
|
37
|
+
/*
|
38
|
+
* Returns the maximum number of keepalive requests a client may make
|
39
|
+
* before the parser refuses to continue.
|
40
|
+
*/
|
41
|
+
static VALUE ka_req(VALUE self)
|
42
|
+
{
|
43
|
+
return ULONG2NUM(keepalive_requests);
|
44
|
+
}
|
45
|
+
|
46
|
+
/*
|
47
|
+
* Sets the maximum number of keepalive requests a client may make.
|
48
|
+
* A special value of +nil+ causes this to be the maximum value
|
49
|
+
* possible (this is architecture-dependent).
|
50
|
+
*/
|
51
|
+
static VALUE set_ka_req(VALUE self, VALUE val)
|
52
|
+
{
|
53
|
+
keepalive_requests = NIL_P(val) ? ULONG_MAX : NUM2ULONG(val);
|
54
|
+
|
55
|
+
return ka_req(self);
|
56
|
+
}
|
57
|
+
|
58
|
+
/*
|
59
|
+
* Sets whether or not the parser will trust X-Forwarded-Proto and
|
60
|
+
* X-Forwarded-SSL headers and set "rack.url_scheme" to "https" accordingly.
|
61
|
+
* Rainbows!/Zbatery installations facing untrusted clients directly
|
62
|
+
* should set this to +false+
|
63
|
+
*/
|
64
|
+
static VALUE set_xftrust(VALUE self, VALUE val)
|
65
|
+
{
|
66
|
+
if (Qtrue == val || Qfalse == val)
|
67
|
+
trust_x_forward = val;
|
68
|
+
else
|
69
|
+
rb_raise(rb_eTypeError, "must be true or false");
|
70
|
+
|
71
|
+
return val;
|
72
|
+
}
|
73
|
+
|
74
|
+
/*
|
75
|
+
* returns whether or not the parser will trust X-Forwarded-Proto and
|
76
|
+
* X-Forwarded-SSL headers and set "rack.url_scheme" to "https" accordingly
|
77
|
+
*/
|
78
|
+
static VALUE xftrust(VALUE self)
|
79
|
+
{
|
80
|
+
return trust_x_forward;
|
81
|
+
}
|
82
|
+
|
28
83
|
/* keep this small for Rainbows! since every client has one */
|
29
84
|
struct http_parser {
|
30
85
|
int cs; /* Ragel internal state */
|
31
86
|
unsigned int flags;
|
87
|
+
unsigned long nr_requests;
|
32
88
|
size_t mark;
|
33
89
|
size_t offset;
|
34
90
|
union { /* these 2 fields don't nest */
|
@@ -391,42 +447,85 @@ static struct http_parser *data_get(VALUE self)
|
|
391
447
|
return hp;
|
392
448
|
}
|
393
449
|
|
394
|
-
|
450
|
+
/*
|
451
|
+
* set rack.url_scheme to "https" or "http", no others are allowed by Rack
|
452
|
+
* this resembles the Rack::Request#scheme method as of rack commit
|
453
|
+
* 35bb5ba6746b5d346de9202c004cc926039650c7
|
454
|
+
*/
|
455
|
+
static void set_url_scheme(VALUE env, VALUE *server_port)
|
395
456
|
{
|
396
|
-
VALUE
|
397
|
-
VALUE server_name = g_localhost;
|
398
|
-
VALUE server_port = g_port_80;
|
457
|
+
VALUE scheme = rb_hash_aref(env, g_rack_url_scheme);
|
399
458
|
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
459
|
+
if (NIL_P(scheme)) {
|
460
|
+
if (trust_x_forward == Qfalse) {
|
461
|
+
scheme = g_http;
|
462
|
+
} else {
|
463
|
+
scheme = rb_hash_aref(env, g_http_x_forwarded_ssl);
|
464
|
+
if (!NIL_P(scheme) && STR_CSTR_EQ(scheme, "on")) {
|
465
|
+
*server_port = g_port_443;
|
466
|
+
scheme = g_https;
|
467
|
+
} else {
|
468
|
+
scheme = rb_hash_aref(env, g_http_x_forwarded_proto);
|
469
|
+
if (NIL_P(scheme)) {
|
470
|
+
scheme = g_http;
|
471
|
+
} else {
|
472
|
+
long len = RSTRING_LEN(scheme);
|
473
|
+
if (len >= 5 && !memcmp(RSTRING_PTR(scheme), "https", 5)) {
|
474
|
+
if (len != 5)
|
475
|
+
scheme = g_https;
|
476
|
+
*server_port = g_port_443;
|
477
|
+
} else {
|
478
|
+
scheme = g_http;
|
479
|
+
}
|
480
|
+
}
|
481
|
+
}
|
482
|
+
}
|
483
|
+
rb_hash_aset(env, g_rack_url_scheme, scheme);
|
484
|
+
} else if (STR_CSTR_EQ(scheme, "https")) {
|
485
|
+
*server_port = g_port_443;
|
410
486
|
} else {
|
411
|
-
assert(server_port == g_port_80 && "server_port not set");
|
487
|
+
assert(*server_port == g_port_80 && "server_port not set");
|
412
488
|
}
|
489
|
+
}
|
490
|
+
|
491
|
+
/*
|
492
|
+
* Parse and set the SERVER_NAME and SERVER_PORT variables
|
493
|
+
* Not supporting X-Forwarded-Host/X-Forwarded-Port in here since
|
494
|
+
* anybody who needs them is using an unsupported configuration and/or
|
495
|
+
* incompetent. Rack::Request will handle X-Forwarded-{Port,Host} just
|
496
|
+
* fine.
|
497
|
+
*/
|
498
|
+
static void set_server_vars(VALUE env, VALUE *server_port)
|
499
|
+
{
|
500
|
+
VALUE server_name = g_localhost;
|
501
|
+
VALUE host = rb_hash_aref(env, g_http_host);
|
502
|
+
|
503
|
+
if (!NIL_P(host)) {
|
504
|
+
char *host_ptr = RSTRING_PTR(host);
|
505
|
+
long host_len = RSTRING_LEN(host);
|
506
|
+
char *colon = memchr(host_ptr, ':', host_len);
|
413
507
|
|
414
|
-
/* parse and set the SERVER_NAME and SERVER_PORT variables */
|
415
|
-
temp = rb_hash_aref(hp->env, g_http_host);
|
416
|
-
if (!NIL_P(temp)) {
|
417
|
-
char *colon = memchr(RSTRING_PTR(temp), ':', RSTRING_LEN(temp));
|
418
508
|
if (colon) {
|
419
|
-
long port_start = colon -
|
509
|
+
long port_start = colon - host_ptr + 1;
|
420
510
|
|
421
|
-
server_name = rb_str_substr(
|
422
|
-
if ((
|
423
|
-
server_port = rb_str_substr(
|
511
|
+
server_name = rb_str_substr(host, 0, colon - host_ptr);
|
512
|
+
if ((host_len - port_start) > 0)
|
513
|
+
*server_port = rb_str_substr(host, port_start, host_len);
|
424
514
|
} else {
|
425
|
-
server_name =
|
515
|
+
server_name = host;
|
426
516
|
}
|
427
517
|
}
|
428
|
-
rb_hash_aset(
|
429
|
-
rb_hash_aset(
|
518
|
+
rb_hash_aset(env, g_server_name, server_name);
|
519
|
+
rb_hash_aset(env, g_server_port, *server_port);
|
520
|
+
}
|
521
|
+
|
522
|
+
static void finalize_header(struct http_parser *hp)
|
523
|
+
{
|
524
|
+
VALUE server_port = g_port_80;
|
525
|
+
|
526
|
+
set_url_scheme(hp->env, &server_port);
|
527
|
+
set_server_vars(hp->env, &server_port);
|
528
|
+
|
430
529
|
if (!HP_FL_TEST(hp, HASHEADER))
|
431
530
|
rb_hash_aset(hp->env, g_server_protocol, g_http_09);
|
432
531
|
|
@@ -464,6 +563,7 @@ static VALUE HttpParser_init(VALUE self)
|
|
464
563
|
http_parser_init(hp);
|
465
564
|
hp->buf = rb_str_new(NULL, 0);
|
466
565
|
hp->env = rb_hash_new();
|
566
|
+
hp->nr_requests = keepalive_requests;
|
467
567
|
|
468
568
|
return self;
|
469
569
|
}
|
@@ -537,6 +637,11 @@ static VALUE HttpParser_parse(VALUE self)
|
|
537
637
|
struct http_parser *hp = data_get(self);
|
538
638
|
VALUE data = hp->buf;
|
539
639
|
|
640
|
+
if (HP_FL_TEST(hp, TO_CLEAR)) {
|
641
|
+
http_parser_init(hp);
|
642
|
+
rb_funcall(hp->env, id_clear, 0);
|
643
|
+
}
|
644
|
+
|
540
645
|
http_parser_execute(hp, RSTRING_PTR(data), RSTRING_LEN(data));
|
541
646
|
VALIDATE_MAX_LENGTH(hp->offset, HEADER);
|
542
647
|
|
@@ -622,15 +727,16 @@ static VALUE HttpParser_keepalive(VALUE self)
|
|
622
727
|
* parser.next? => true or false
|
623
728
|
*
|
624
729
|
* Exactly like HttpParser#keepalive?, except it will reset the internal
|
625
|
-
* parser state if it returns true.
|
730
|
+
* parser state on next parse if it returns true. It will also respect
|
731
|
+
* the maximum *keepalive_requests* value and return false if that is
|
732
|
+
* reached.
|
626
733
|
*/
|
627
734
|
static VALUE HttpParser_next(VALUE self)
|
628
735
|
{
|
629
736
|
struct http_parser *hp = data_get(self);
|
630
737
|
|
631
|
-
if (HP_FL_ALL(hp, KEEPALIVE)) {
|
632
|
-
|
633
|
-
rb_funcall(hp->env, id_clear, 0);
|
738
|
+
if ((HP_FL_ALL(hp, KEEPALIVE)) && (hp->nr_requests-- != 0)) {
|
739
|
+
HP_FL_SET(hp, TO_CLEAR);
|
634
740
|
return Qtrue;
|
635
741
|
}
|
636
742
|
return Qfalse;
|
@@ -775,6 +881,15 @@ void Init_unicorn_http(void)
|
|
775
881
|
*/
|
776
882
|
rb_define_const(cHttpParser, "LENGTH_MAX", OFFT2NUM(UH_OFF_T_MAX));
|
777
883
|
|
884
|
+
/* default value for keepalive_requests */
|
885
|
+
rb_define_const(cHttpParser, "KEEPALIVE_REQUESTS_DEFAULT",
|
886
|
+
ULONG2NUM(keepalive_requests));
|
887
|
+
|
888
|
+
rb_define_singleton_method(cHttpParser, "keepalive_requests", ka_req, 0);
|
889
|
+
rb_define_singleton_method(cHttpParser, "keepalive_requests=", set_ka_req, 1);
|
890
|
+
rb_define_singleton_method(cHttpParser, "trust_x_forwarded=", set_xftrust, 1);
|
891
|
+
rb_define_singleton_method(cHttpParser, "trust_x_forwarded?", xftrust, 0);
|
892
|
+
|
778
893
|
init_common_fields();
|
779
894
|
SET_GLOBAL(g_http_host, "HOST");
|
780
895
|
SET_GLOBAL(g_http_trailer, "TRAILER");
|
data/lib/unicorn/configurator.rb
CHANGED
@@ -10,9 +10,10 @@ require 'logger'
|
|
10
10
|
# http://unicorn.bogomips.org/examples/nginx.conf
|
11
11
|
class Unicorn::Configurator
|
12
12
|
include Unicorn
|
13
|
-
attr_accessor :set, :config_file, :after_reload
|
14
13
|
|
15
14
|
# :stopdoc:
|
15
|
+
attr_accessor :set, :config_file, :after_reload
|
16
|
+
|
16
17
|
# used to stash stuff for deferred processing of cli options in
|
17
18
|
# config.ru after "working_directory" is bound. Do not rely on
|
18
19
|
# this being around later on...
|
@@ -42,6 +43,7 @@ class Unicorn::Configurator
|
|
42
43
|
:preload_app => false,
|
43
44
|
:rewindable_input => true, # for Rack 2.x: (Rack::VERSION[0] <= 1),
|
44
45
|
:client_body_buffer_size => Unicorn::Const::MAX_BODY,
|
46
|
+
:trust_x_forwarded => true,
|
45
47
|
}
|
46
48
|
#:startdoc:
|
47
49
|
|
@@ -448,6 +450,15 @@ class Unicorn::Configurator
|
|
448
450
|
set[:user] = [ user, group ]
|
449
451
|
end
|
450
452
|
|
453
|
+
# Sets whether or not the parser will trust X-Forwarded-Proto and
|
454
|
+
# X-Forwarded-SSL headers and set "rack.url_scheme" to "https" accordingly.
|
455
|
+
# Rainbows!/Zbatery installations facing untrusted clients directly
|
456
|
+
# should set this to +false+. This is +true+ by default as Unicorn
|
457
|
+
# is designed to only sit behind trusted nginx proxies.
|
458
|
+
def trust_x_forwarded(bool)
|
459
|
+
set_bool(:trust_x_forwarded, bool)
|
460
|
+
end
|
461
|
+
|
451
462
|
# expands "unix:path/to/foo" to a socket relative to the current path
|
452
463
|
# expands pathnames of sockets if relative to "~" or "~username"
|
453
464
|
# expands "*:port and ":port" to "0.0.0.0:port"
|
@@ -472,7 +483,7 @@ class Unicorn::Configurator
|
|
472
483
|
end
|
473
484
|
|
474
485
|
private
|
475
|
-
def set_int(var, n, min)
|
486
|
+
def set_int(var, n, min) #:nodoc:
|
476
487
|
Integer === n or raise ArgumentError, "not an integer: #{var}=#{n.inspect}"
|
477
488
|
n >= min or raise ArgumentError, "too low (< #{min}): #{var}=#{n.inspect}"
|
478
489
|
set[var] = n
|
data/lib/unicorn/const.rb
CHANGED
@@ -7,8 +7,8 @@
|
|
7
7
|
# improve things much compared to constants.
|
8
8
|
module Unicorn::Const
|
9
9
|
|
10
|
-
# The current version of Unicorn, currently 3.1
|
11
|
-
UNICORN_VERSION = "3.1
|
10
|
+
# The current version of Unicorn, currently 3.2.1
|
11
|
+
UNICORN_VERSION = "3.2.1"
|
12
12
|
|
13
13
|
# default TCP listen host address (0.0.0.0, all interfaces)
|
14
14
|
DEFAULT_HOST = "0.0.0.0"
|
@@ -9,8 +9,6 @@ require 'time'
|
|
9
9
|
#
|
10
10
|
# Most header correctness (including Content-Length and Content-Type)
|
11
11
|
# is the job of Rack, with the exception of the "Date" and "Status" header.
|
12
|
-
#
|
13
|
-
# TODO: allow keepalive
|
14
12
|
module Unicorn::HttpResponse
|
15
13
|
|
16
14
|
# Every standard HTTP code mapped to the appropriate message.
|
data/lib/unicorn/http_server.rb
CHANGED
@@ -73,19 +73,17 @@ class Unicorn::HttpServer
|
|
73
73
|
# Unicorn::HttpServer::START_CTX[0] = "/home/bofh/1.9.2/bin/unicorn"
|
74
74
|
START_CTX = {
|
75
75
|
:argv => ARGV.map { |arg| arg.dup },
|
76
|
-
:cwd => lambda {
|
77
|
-
# favor ENV['PWD'] since it is (usually) symlink aware for
|
78
|
-
# Capistrano and like systems
|
79
|
-
begin
|
80
|
-
a = File.stat(pwd = ENV['PWD'])
|
81
|
-
b = File.stat(Dir.pwd)
|
82
|
-
a.ino == b.ino && a.dev == b.dev ? pwd : Dir.pwd
|
83
|
-
rescue
|
84
|
-
Dir.pwd
|
85
|
-
end
|
86
|
-
}.call,
|
87
76
|
0 => $0.dup,
|
88
77
|
}
|
78
|
+
# We favor ENV['PWD'] since it is (usually) symlink aware for Capistrano
|
79
|
+
# and like systems
|
80
|
+
START_CTX[:cwd] = begin
|
81
|
+
a = File.stat(pwd = ENV['PWD'])
|
82
|
+
b = File.stat(Dir.pwd)
|
83
|
+
a.ino == b.ino && a.dev == b.dev ? pwd : Dir.pwd
|
84
|
+
rescue
|
85
|
+
Dir.pwd
|
86
|
+
end
|
89
87
|
|
90
88
|
# Creates a working server on host:port (strange things happen if
|
91
89
|
# port isn't a Number). Use HttpServer::run to start the server and
|
@@ -372,6 +370,14 @@ class Unicorn::HttpServer
|
|
372
370
|
Unicorn::TeeInput.client_body_buffer_size = bytes
|
373
371
|
end
|
374
372
|
|
373
|
+
def trust_x_forwarded
|
374
|
+
Unicorn::HttpParser.trust_x_forwarded?
|
375
|
+
end
|
376
|
+
|
377
|
+
def trust_x_forwarded=(bool)
|
378
|
+
Unicorn::HttpParser.trust_x_forwarded = bool
|
379
|
+
end
|
380
|
+
|
375
381
|
private
|
376
382
|
|
377
383
|
# wait for a signal hander to wake us up and then consume the pipe
|
data/script/isolate_for_tests
CHANGED
@@ -0,0 +1,30 @@
|
|
1
|
+
#!/bin/sh
|
2
|
+
. ./test-lib.sh
|
3
|
+
t_plan 5 "trust_x_forwarded=false configuration test"
|
4
|
+
|
5
|
+
t_begin "setup and start" && {
|
6
|
+
unicorn_setup
|
7
|
+
echo "trust_x_forwarded false" >> $unicorn_config
|
8
|
+
unicorn -D -c $unicorn_config env.ru
|
9
|
+
unicorn_wait_start
|
10
|
+
}
|
11
|
+
|
12
|
+
t_begin "spoofed request with X-Forwarded-Proto does not trigger" && {
|
13
|
+
curl -H 'X-Forwarded-Proto: https' http://$listen/ | \
|
14
|
+
grep -F '"rack.url_scheme"=>"http"'
|
15
|
+
}
|
16
|
+
|
17
|
+
t_begin "spoofed request with X-Forwarded-SSL does not trigger" && {
|
18
|
+
curl -H 'X-Forwarded-SSL: on' http://$listen/ | \
|
19
|
+
grep -F '"rack.url_scheme"=>"http"'
|
20
|
+
}
|
21
|
+
|
22
|
+
t_begin "killing succeeds" && {
|
23
|
+
kill $unicorn_pid
|
24
|
+
}
|
25
|
+
|
26
|
+
t_begin "check stderr has no errors" && {
|
27
|
+
check_stderr
|
28
|
+
}
|
29
|
+
|
30
|
+
t_done
|
@@ -0,0 +1,30 @@
|
|
1
|
+
#!/bin/sh
|
2
|
+
. ./test-lib.sh
|
3
|
+
t_plan 5 "trust_x_forwarded=true configuration test"
|
4
|
+
|
5
|
+
t_begin "setup and start" && {
|
6
|
+
unicorn_setup
|
7
|
+
echo "trust_x_forwarded true " >> $unicorn_config
|
8
|
+
unicorn -D -c $unicorn_config env.ru
|
9
|
+
unicorn_wait_start
|
10
|
+
}
|
11
|
+
|
12
|
+
t_begin "spoofed request with X-Forwarded-Proto sets 'https'" && {
|
13
|
+
curl -H 'X-Forwarded-Proto: https' http://$listen/ | \
|
14
|
+
grep -F '"rack.url_scheme"=>"https"'
|
15
|
+
}
|
16
|
+
|
17
|
+
t_begin "spoofed request with X-Forwarded-SSL sets 'https'" && {
|
18
|
+
curl -H 'X-Forwarded-SSL: on' http://$listen/ | \
|
19
|
+
grep -F '"rack.url_scheme"=>"https"'
|
20
|
+
}
|
21
|
+
|
22
|
+
t_begin "killing succeeds" && {
|
23
|
+
kill $unicorn_pid
|
24
|
+
}
|
25
|
+
|
26
|
+
t_begin "check stderr has no errors" && {
|
27
|
+
check_stderr
|
28
|
+
}
|
29
|
+
|
30
|
+
t_done
|
@@ -147,6 +147,61 @@ class HttpParserTest < Test::Unit::TestCase
|
|
147
147
|
assert parser.keepalive?
|
148
148
|
end
|
149
149
|
|
150
|
+
def test_parse_xfp_https_chained
|
151
|
+
parser = HttpParser.new
|
152
|
+
req = {}
|
153
|
+
tmp = "GET / HTTP/1.0\r\n" \
|
154
|
+
"X-Forwarded-Proto: https,http\r\n\r\n"
|
155
|
+
assert_equal req, parser.headers(req, tmp)
|
156
|
+
assert_equal '443', req['SERVER_PORT'], req.inspect
|
157
|
+
assert_equal 'https', req['rack.url_scheme'], req.inspect
|
158
|
+
assert_equal '', tmp
|
159
|
+
end
|
160
|
+
|
161
|
+
def test_parse_xfp_https_chained_backwards
|
162
|
+
parser = HttpParser.new
|
163
|
+
req = {}
|
164
|
+
tmp = "GET / HTTP/1.0\r\n" \
|
165
|
+
"X-Forwarded-Proto: http,https\r\n\r\n"
|
166
|
+
assert_equal req, parser.headers(req, tmp)
|
167
|
+
assert_equal '80', req['SERVER_PORT'], req.inspect
|
168
|
+
assert_equal 'http', req['rack.url_scheme'], req.inspect
|
169
|
+
assert_equal '', tmp
|
170
|
+
end
|
171
|
+
|
172
|
+
def test_parse_xfp_gopher_is_ignored
|
173
|
+
parser = HttpParser.new
|
174
|
+
req = {}
|
175
|
+
tmp = "GET / HTTP/1.0\r\n" \
|
176
|
+
"X-Forwarded-Proto: gopher\r\n\r\n"
|
177
|
+
assert_equal req, parser.headers(req, tmp)
|
178
|
+
assert_equal '80', req['SERVER_PORT'], req.inspect
|
179
|
+
assert_equal 'http', req['rack.url_scheme'], req.inspect
|
180
|
+
assert_equal '', tmp
|
181
|
+
end
|
182
|
+
|
183
|
+
def test_parse_x_forwarded_ssl_on
|
184
|
+
parser = HttpParser.new
|
185
|
+
req = {}
|
186
|
+
tmp = "GET / HTTP/1.0\r\n" \
|
187
|
+
"X-Forwarded-Ssl: on\r\n\r\n"
|
188
|
+
assert_equal req, parser.headers(req, tmp)
|
189
|
+
assert_equal '443', req['SERVER_PORT'], req.inspect
|
190
|
+
assert_equal 'https', req['rack.url_scheme'], req.inspect
|
191
|
+
assert_equal '', tmp
|
192
|
+
end
|
193
|
+
|
194
|
+
def test_parse_x_forwarded_ssl_off
|
195
|
+
parser = HttpParser.new
|
196
|
+
req = {}
|
197
|
+
tmp = "GET / HTTP/1.0\r\n" \
|
198
|
+
"X-Forwarded-Ssl: off\r\n\r\n"
|
199
|
+
assert_equal req, parser.headers(req, tmp)
|
200
|
+
assert_equal '80', req['SERVER_PORT'], req.inspect
|
201
|
+
assert_equal 'http', req['rack.url_scheme'], req.inspect
|
202
|
+
assert_equal '', tmp
|
203
|
+
end
|
204
|
+
|
150
205
|
def test_parse_strange_headers
|
151
206
|
parser = HttpParser.new
|
152
207
|
req = {}
|
@@ -8,9 +8,81 @@ include Unicorn
|
|
8
8
|
class HttpParserNgTest < Test::Unit::TestCase
|
9
9
|
|
10
10
|
def setup
|
11
|
+
HttpParser.keepalive_requests = HttpParser::KEEPALIVE_REQUESTS_DEFAULT
|
11
12
|
@parser = HttpParser.new
|
12
13
|
end
|
13
14
|
|
15
|
+
def test_keepalive_requests_default_constant
|
16
|
+
assert_kind_of Integer, HttpParser::KEEPALIVE_REQUESTS_DEFAULT
|
17
|
+
assert HttpParser::KEEPALIVE_REQUESTS_DEFAULT >= 0
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_keepalive_requests_setting
|
21
|
+
HttpParser.keepalive_requests = 0
|
22
|
+
assert_equal 0, HttpParser.keepalive_requests
|
23
|
+
HttpParser.keepalive_requests = nil
|
24
|
+
assert HttpParser.keepalive_requests >= 0xffffffff
|
25
|
+
HttpParser.keepalive_requests = 1
|
26
|
+
assert_equal 1, HttpParser.keepalive_requests
|
27
|
+
HttpParser.keepalive_requests = 666
|
28
|
+
assert_equal 666, HttpParser.keepalive_requests
|
29
|
+
|
30
|
+
assert_raises(TypeError) { HttpParser.keepalive_requests = "666" }
|
31
|
+
assert_raises(TypeError) { HttpParser.keepalive_requests = [] }
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_keepalive_requests_with_next?
|
35
|
+
req = "GET / HTTP/1.1\r\nHost: example.com\r\n\r\n".freeze
|
36
|
+
expect = {
|
37
|
+
"SERVER_NAME" => "example.com",
|
38
|
+
"HTTP_HOST" => "example.com",
|
39
|
+
"rack.url_scheme" => "http",
|
40
|
+
"REQUEST_PATH" => "/",
|
41
|
+
"SERVER_PROTOCOL" => "HTTP/1.1",
|
42
|
+
"PATH_INFO" => "/",
|
43
|
+
"HTTP_VERSION" => "HTTP/1.1",
|
44
|
+
"REQUEST_URI" => "/",
|
45
|
+
"SERVER_PORT" => "80",
|
46
|
+
"REQUEST_METHOD" => "GET",
|
47
|
+
"QUERY_STRING" => ""
|
48
|
+
}.freeze
|
49
|
+
HttpParser::KEEPALIVE_REQUESTS_DEFAULT.times do |nr|
|
50
|
+
@parser.buf << req
|
51
|
+
assert_equal expect, @parser.parse
|
52
|
+
assert @parser.next?
|
53
|
+
end
|
54
|
+
@parser.buf << req
|
55
|
+
assert_equal expect, @parser.parse
|
56
|
+
assert ! @parser.next?
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_fewer_keepalive_requests_with_next?
|
60
|
+
HttpParser.keepalive_requests = 5
|
61
|
+
@parser = HttpParser.new
|
62
|
+
req = "GET / HTTP/1.1\r\nHost: example.com\r\n\r\n".freeze
|
63
|
+
expect = {
|
64
|
+
"SERVER_NAME" => "example.com",
|
65
|
+
"HTTP_HOST" => "example.com",
|
66
|
+
"rack.url_scheme" => "http",
|
67
|
+
"REQUEST_PATH" => "/",
|
68
|
+
"SERVER_PROTOCOL" => "HTTP/1.1",
|
69
|
+
"PATH_INFO" => "/",
|
70
|
+
"HTTP_VERSION" => "HTTP/1.1",
|
71
|
+
"REQUEST_URI" => "/",
|
72
|
+
"SERVER_PORT" => "80",
|
73
|
+
"REQUEST_METHOD" => "GET",
|
74
|
+
"QUERY_STRING" => ""
|
75
|
+
}.freeze
|
76
|
+
5.times do |nr|
|
77
|
+
@parser.buf << req
|
78
|
+
assert_equal expect, @parser.parse
|
79
|
+
assert @parser.next?
|
80
|
+
end
|
81
|
+
@parser.buf << req
|
82
|
+
assert_equal expect, @parser.parse
|
83
|
+
assert ! @parser.next?
|
84
|
+
end
|
85
|
+
|
14
86
|
def test_default_keepalive_is_off
|
15
87
|
assert ! @parser.keepalive?
|
16
88
|
assert ! @parser.next?
|
@@ -504,9 +576,10 @@ class HttpParserNgTest < Test::Unit::TestCase
|
|
504
576
|
end
|
505
577
|
|
506
578
|
def test_pipelined_requests
|
579
|
+
host = "example.com"
|
507
580
|
expect = {
|
508
|
-
"HTTP_HOST" =>
|
509
|
-
"SERVER_NAME" =>
|
581
|
+
"HTTP_HOST" => host,
|
582
|
+
"SERVER_NAME" => host,
|
510
583
|
"REQUEST_PATH" => "/",
|
511
584
|
"rack.url_scheme" => "http",
|
512
585
|
"SERVER_PROTOCOL" => "HTTP/1.1",
|
@@ -517,15 +590,21 @@ class HttpParserNgTest < Test::Unit::TestCase
|
|
517
590
|
"REQUEST_METHOD" => "GET",
|
518
591
|
"QUERY_STRING" => ""
|
519
592
|
}
|
520
|
-
|
521
|
-
|
593
|
+
req1 = "GET / HTTP/1.1\r\nHost: example.com\r\n\r\n"
|
594
|
+
req2 = "GET / HTTP/1.1\r\nHost: www.example.com\r\n\r\n"
|
595
|
+
@parser.buf << (req1 + req2)
|
522
596
|
env1 = @parser.parse.dup
|
523
597
|
assert_equal expect, env1
|
524
|
-
assert_equal
|
598
|
+
assert_equal req2, @parser.buf
|
525
599
|
assert ! @parser.env.empty?
|
526
600
|
assert @parser.next?
|
527
|
-
assert @parser.
|
601
|
+
assert @parser.keepalive?
|
602
|
+
assert @parser.headers?
|
603
|
+
assert_equal expect, @parser.env
|
528
604
|
env2 = @parser.parse.dup
|
605
|
+
host.replace "www.example.com"
|
606
|
+
assert_equal "www.example.com", expect["HTTP_HOST"]
|
607
|
+
assert_equal "www.example.com", expect["SERVER_NAME"]
|
529
608
|
assert_equal expect, env2
|
530
609
|
assert_equal "", @parser.buf
|
531
610
|
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# -*- encoding: binary -*-
|
2
|
+
require 'test/test_helper'
|
3
|
+
|
4
|
+
include Unicorn
|
5
|
+
|
6
|
+
class HttpParserXFTrustTest < Test::Unit::TestCase
|
7
|
+
def setup
|
8
|
+
assert HttpParser.trust_x_forwarded?
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_xf_trust_false_xfp
|
12
|
+
HttpParser.trust_x_forwarded = false
|
13
|
+
parser = HttpParser.new
|
14
|
+
parser.buf << "GET / HTTP/1.1\r\nHost: foo:\r\n" \
|
15
|
+
"X-Forwarded-Proto: https\r\n\r\n"
|
16
|
+
env = parser.parse
|
17
|
+
assert_kind_of Hash, env
|
18
|
+
assert_equal 'foo', env['SERVER_NAME']
|
19
|
+
assert_equal '80', env['SERVER_PORT']
|
20
|
+
assert_equal 'http', env['rack.url_scheme']
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_xf_trust_false_xfs
|
24
|
+
HttpParser.trust_x_forwarded = false
|
25
|
+
parser = HttpParser.new
|
26
|
+
parser.buf << "GET / HTTP/1.1\r\nHost: foo:\r\n" \
|
27
|
+
"X-Forwarded-SSL: on\r\n\r\n"
|
28
|
+
env = parser.parse
|
29
|
+
assert_kind_of Hash, env
|
30
|
+
assert_equal 'foo', env['SERVER_NAME']
|
31
|
+
assert_equal '80', env['SERVER_PORT']
|
32
|
+
assert_equal 'http', env['rack.url_scheme']
|
33
|
+
end
|
34
|
+
|
35
|
+
def teardown
|
36
|
+
HttpParser.trust_x_forwarded = true
|
37
|
+
end
|
38
|
+
end
|
data/unicorn.gemspec
CHANGED
@@ -1,7 +1,9 @@
|
|
1
1
|
# -*- encoding: binary -*-
|
2
|
-
|
3
2
|
ENV["VERSION"] or abort "VERSION= must be specified"
|
4
3
|
manifest = File.readlines('.manifest').map! { |x| x.chomp! }
|
4
|
+
require 'wrongdoc'
|
5
|
+
extend Wrongdoc::Gemspec
|
6
|
+
name, summary, title = readme_metadata
|
5
7
|
|
6
8
|
# don't bother with tests that fork, not worth our time to get working
|
7
9
|
# with `gem check -t` ... (of course we care for them when testing with
|
@@ -12,35 +14,20 @@ end.compact
|
|
12
14
|
|
13
15
|
Gem::Specification.new do |s|
|
14
16
|
s.name = %q{unicorn}
|
15
|
-
s.version = ENV["VERSION"]
|
16
|
-
|
17
|
-
s.
|
17
|
+
s.version = ENV["VERSION"].dup
|
18
|
+
s.authors = ["#{name} hackers"]
|
19
|
+
s.summary = summary
|
18
20
|
s.date = Time.now.utc.strftime('%Y-%m-%d')
|
19
|
-
s.description =
|
21
|
+
s.description = readme_description
|
20
22
|
s.email = %q{mongrel-unicorn@rubyforge.org}
|
21
23
|
s.executables = %w(unicorn unicorn_rails)
|
22
24
|
s.extensions = %w(ext/unicorn_http/extconf.rb)
|
23
|
-
|
24
|
-
s.extra_rdoc_files = File.readlines('.document').map! do |x|
|
25
|
-
x.chomp!
|
26
|
-
if File.directory?(x)
|
27
|
-
manifest.grep(%r{\A#{x}/})
|
28
|
-
elsif File.file?(x)
|
29
|
-
x
|
30
|
-
else
|
31
|
-
nil
|
32
|
-
end
|
33
|
-
end.flatten.compact
|
34
|
-
|
25
|
+
s.extra_rdoc_files = extra_rdoc_files(manifest)
|
35
26
|
s.files = manifest
|
36
|
-
s.homepage =
|
37
|
-
|
38
|
-
summary = %q{Rack HTTP server for fast clients and Unix}
|
39
|
-
s.rdoc_options = [ "-t", "Unicorn: #{summary}" ]
|
27
|
+
s.homepage = Wrongdoc.config[:rdoc_url]
|
28
|
+
s.rdoc_options = rdoc_options
|
40
29
|
s.require_paths = %w(lib ext)
|
41
30
|
s.rubyforge_project = %q{mongrel}
|
42
|
-
s.summary = summary
|
43
|
-
|
44
31
|
s.test_files = test_files
|
45
32
|
|
46
33
|
# for people that are absolutely stuck on Rails 2.3.2 and can't
|
@@ -48,9 +35,10 @@ Gem::Specification.new do |s|
|
|
48
35
|
# commented out. Nevertheless, upgrading to Rails 2.3.4 or later is
|
49
36
|
# *strongly* recommended for security reasons.
|
50
37
|
s.add_dependency(%q<rack>)
|
51
|
-
s.add_dependency(%q<kgio>, '~> 2.
|
38
|
+
s.add_dependency(%q<kgio>, '~> 2.1')
|
52
39
|
|
53
40
|
s.add_development_dependency('isolate', '~> 3.0.0')
|
41
|
+
s.add_development_dependency('wrongdoc', '~> 1.0.1')
|
54
42
|
|
55
43
|
# s.licenses = %w(GPLv2 Ruby) # licenses= method is not in older RubyGems
|
56
44
|
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: unicorn
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 13
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 3
|
8
|
+
- 2
|
8
9
|
- 1
|
9
|
-
|
10
|
-
version: 3.1.0
|
10
|
+
version: 3.2.1
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Unicorn hackers
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2010-12-
|
18
|
+
date: 2010-12-26 00:00:00 +00:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
@@ -40,12 +40,11 @@ dependencies:
|
|
40
40
|
requirements:
|
41
41
|
- - ~>
|
42
42
|
- !ruby/object:Gem::Version
|
43
|
-
hash:
|
43
|
+
hash: 1
|
44
44
|
segments:
|
45
45
|
- 2
|
46
|
-
-
|
47
|
-
|
48
|
-
version: 2.0.0
|
46
|
+
- 1
|
47
|
+
version: "2.1"
|
49
48
|
type: :runtime
|
50
49
|
version_requirements: *id002
|
51
50
|
- !ruby/object:Gem::Dependency
|
@@ -64,12 +63,28 @@ dependencies:
|
|
64
63
|
version: 3.0.0
|
65
64
|
type: :development
|
66
65
|
version_requirements: *id003
|
66
|
+
- !ruby/object:Gem::Dependency
|
67
|
+
name: wrongdoc
|
68
|
+
prerelease: false
|
69
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
70
|
+
none: false
|
71
|
+
requirements:
|
72
|
+
- - ~>
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
hash: 21
|
75
|
+
segments:
|
76
|
+
- 1
|
77
|
+
- 0
|
78
|
+
- 1
|
79
|
+
version: 1.0.1
|
80
|
+
type: :development
|
81
|
+
version_requirements: *id004
|
67
82
|
description: |-
|
68
|
-
Unicorn is an HTTP server for Rack applications designed to only serve
|
83
|
+
\Unicorn is an HTTP server for Rack applications designed to only serve
|
69
84
|
fast clients on low-latency, high-bandwidth connections and take
|
70
85
|
advantage of features in Unix/Unix-like kernels. Slow clients should
|
71
86
|
only be served by placing a reverse proxy capable of fully buffering
|
72
|
-
both the the request and response in between Unicorn and slow clients.
|
87
|
+
both the the request and response in between \Unicorn and slow clients.
|
73
88
|
email: mongrel-unicorn@rubyforge.org
|
74
89
|
executables:
|
75
90
|
- unicorn
|
@@ -90,6 +105,7 @@ extra_rdoc_files:
|
|
90
105
|
- TODO
|
91
106
|
- NEWS
|
92
107
|
- ChangeLog
|
108
|
+
- LATEST
|
93
109
|
- lib/unicorn.rb
|
94
110
|
- lib/unicorn/app/exec_cgi.rb
|
95
111
|
- lib/unicorn/app/inetd.rb
|
@@ -119,6 +135,7 @@ files:
|
|
119
135
|
- .gitignore
|
120
136
|
- .mailmap
|
121
137
|
- .manifest
|
138
|
+
- .wrongdoc.yml
|
122
139
|
- CONTRIBUTORS
|
123
140
|
- COPYING
|
124
141
|
- ChangeLog
|
@@ -134,6 +151,7 @@ files:
|
|
134
151
|
- HACKING
|
135
152
|
- ISSUES
|
136
153
|
- KNOWN_ISSUES
|
154
|
+
- LATEST
|
137
155
|
- LICENSE
|
138
156
|
- NEWS
|
139
157
|
- PHILOSOPHY
|
@@ -250,6 +268,8 @@ files:
|
|
250
268
|
- t/t0014-rewindable-input-true.sh
|
251
269
|
- t/t0014.ru
|
252
270
|
- t/t0015-configurator-internals.sh
|
271
|
+
- t/t0016-trust-x-forwarded-false.sh
|
272
|
+
- t/t0017-trust-x-forwarded-true.sh
|
253
273
|
- t/t0100-rack-input-tests.sh
|
254
274
|
- t/t0116-client_body_buffer_size.sh
|
255
275
|
- t/t0116.ru
|
@@ -347,6 +367,7 @@ files:
|
|
347
367
|
- test/unit/test_configurator.rb
|
348
368
|
- test/unit/test_http_parser.rb
|
349
369
|
- test/unit/test_http_parser_ng.rb
|
370
|
+
- test/unit/test_http_parser_xftrust.rb
|
350
371
|
- test/unit/test_request.rb
|
351
372
|
- test/unit/test_response.rb
|
352
373
|
- test/unit/test_server.rb
|
@@ -365,6 +386,8 @@ post_install_message:
|
|
365
386
|
rdoc_options:
|
366
387
|
- -t
|
367
388
|
- "Unicorn: Rack HTTP server for fast clients and Unix"
|
389
|
+
- -W
|
390
|
+
- http://git.bogomips.org/cgit/unicorn.git/tree/%s
|
368
391
|
require_paths:
|
369
392
|
- lib
|
370
393
|
- ext
|
@@ -397,6 +420,7 @@ test_files:
|
|
397
420
|
- test/unit/test_configurator.rb
|
398
421
|
- test/unit/test_http_parser.rb
|
399
422
|
- test/unit/test_http_parser_ng.rb
|
423
|
+
- test/unit/test_http_parser_xftrust.rb
|
400
424
|
- test/unit/test_request.rb
|
401
425
|
- test/unit/test_response.rb
|
402
426
|
- test/unit/test_server.rb
|