unicorn 3.1.0 → 3.2.1
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/.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
|