puma 0.8.2-java
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of puma might be problematic. Click here for more details.
- data/.gemtest +0 -0
- data/COPYING +55 -0
- data/Gemfile +6 -0
- data/History.txt +69 -0
- data/LICENSE +26 -0
- data/Manifest.txt +60 -0
- data/README.md +60 -0
- data/Rakefile +12 -0
- data/TODO +5 -0
- data/bin/puma +15 -0
- data/examples/builder.rb +29 -0
- data/examples/camping/README +3 -0
- data/examples/camping/blog.rb +294 -0
- data/examples/camping/tepee.rb +149 -0
- data/examples/httpd.conf +474 -0
- data/examples/mime.yaml +3 -0
- data/examples/mongrel.conf +9 -0
- data/examples/monitrc +57 -0
- data/examples/random_thrash.rb +19 -0
- data/examples/simpletest.rb +52 -0
- data/examples/webrick_compare.rb +20 -0
- data/ext/puma_http11/PumaHttp11Service.java +13 -0
- data/ext/puma_http11/ext_help.h +15 -0
- data/ext/puma_http11/extconf.rb +5 -0
- data/ext/puma_http11/http11_parser.c +1225 -0
- data/ext/puma_http11/http11_parser.h +63 -0
- data/ext/puma_http11/http11_parser.java.rl +161 -0
- data/ext/puma_http11/http11_parser.rl +146 -0
- data/ext/puma_http11/http11_parser_common.rl +54 -0
- data/ext/puma_http11/org/jruby/puma/Http11.java +225 -0
- data/ext/puma_http11/org/jruby/puma/Http11Parser.java +488 -0
- data/ext/puma_http11/puma_http11.c +482 -0
- data/lib/puma.rb +18 -0
- data/lib/puma/cli.rb +164 -0
- data/lib/puma/const.rb +132 -0
- data/lib/puma/events.rb +36 -0
- data/lib/puma/gems.rb +20 -0
- data/lib/puma/mime_types.yml +616 -0
- data/lib/puma/rack_patch.rb +22 -0
- data/lib/puma/server.rb +429 -0
- data/lib/puma/thread_pool.rb +95 -0
- data/lib/puma/utils.rb +44 -0
- data/lib/puma_http11.jar +0 -0
- data/lib/rack/handler/puma.rb +48 -0
- data/puma.gemspec +40 -0
- data/tasks/gem.rake +24 -0
- data/tasks/java.rake +12 -0
- data/tasks/native.rake +36 -0
- data/tasks/ragel.rake +24 -0
- data/test/lobster.ru +4 -0
- data/test/mime.yaml +3 -0
- data/test/test_cli.rb +19 -0
- data/test/test_http10.rb +27 -0
- data/test/test_http11.rb +151 -0
- data/test/test_persistent.rb +205 -0
- data/test/test_rack_handler.rb +10 -0
- data/test/test_rack_server.rb +122 -0
- data/test/test_thread_pool.rb +102 -0
- data/test/test_unix_socket.rb +37 -0
- data/test/test_ws.rb +97 -0
- data/test/testhelp.rb +41 -0
- data/tools/trickletest.rb +45 -0
- metadata +163 -0
@@ -0,0 +1,95 @@
|
|
1
|
+
require 'thread'
|
2
|
+
|
3
|
+
module Puma
|
4
|
+
class ThreadPool
|
5
|
+
def initialize(min, max, &blk)
|
6
|
+
@todo = Queue.new
|
7
|
+
@mutex = Mutex.new
|
8
|
+
|
9
|
+
@spawned = 0
|
10
|
+
@min = min
|
11
|
+
@max = max
|
12
|
+
@block = blk
|
13
|
+
|
14
|
+
@trim_requested = 0
|
15
|
+
|
16
|
+
@workers = []
|
17
|
+
|
18
|
+
min.times { spawn_thread }
|
19
|
+
end
|
20
|
+
|
21
|
+
attr_reader :spawned
|
22
|
+
|
23
|
+
def backlog
|
24
|
+
@todo.size
|
25
|
+
end
|
26
|
+
|
27
|
+
Stop = Object.new
|
28
|
+
Trim = Object.new
|
29
|
+
|
30
|
+
def spawn_thread
|
31
|
+
@mutex.synchronize do
|
32
|
+
@spawned += 1
|
33
|
+
end
|
34
|
+
|
35
|
+
th = Thread.new do
|
36
|
+
todo = @todo
|
37
|
+
block = @block
|
38
|
+
|
39
|
+
while true
|
40
|
+
work = todo.pop
|
41
|
+
|
42
|
+
case work
|
43
|
+
when Stop
|
44
|
+
break
|
45
|
+
when Trim
|
46
|
+
@mutex.synchronize do
|
47
|
+
@trim_requested -= 1
|
48
|
+
end
|
49
|
+
|
50
|
+
break
|
51
|
+
else
|
52
|
+
block.call work
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
@mutex.synchronize do
|
57
|
+
@spawned -= 1
|
58
|
+
@workers.delete th
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
@mutex.synchronize { @workers << th }
|
63
|
+
|
64
|
+
th
|
65
|
+
end
|
66
|
+
|
67
|
+
def <<(work)
|
68
|
+
if @todo.num_waiting == 0 and @spawned < @max
|
69
|
+
spawn_thread
|
70
|
+
end
|
71
|
+
|
72
|
+
@todo << work
|
73
|
+
end
|
74
|
+
|
75
|
+
def trim
|
76
|
+
@mutex.synchronize do
|
77
|
+
if @spawned - @trim_requested > @min
|
78
|
+
@trim_requested += 1
|
79
|
+
@todo << Trim
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def shutdown
|
85
|
+
@spawned.times do
|
86
|
+
@todo << Stop
|
87
|
+
end
|
88
|
+
|
89
|
+
@workers.each { |w| w.join }
|
90
|
+
|
91
|
+
@spawned = 0
|
92
|
+
@workers = []
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
data/lib/puma/utils.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
module Puma
|
2
|
+
module Utils
|
3
|
+
# Performs URI escaping so that you can construct proper
|
4
|
+
# query strings faster. Use this rather than the cgi.rb
|
5
|
+
# version since it's faster. (Stolen from Camping).
|
6
|
+
def self.escape(s)
|
7
|
+
s.to_s.gsub(/([^ a-zA-Z0-9_.-]+)/n) {
|
8
|
+
'%'+$1.unpack('H2'*$1.size).join('%').upcase
|
9
|
+
}.tr(' ', '+')
|
10
|
+
end
|
11
|
+
|
12
|
+
|
13
|
+
# Unescapes a URI escaped string. (Stolen from Camping).
|
14
|
+
def self.unescape(s)
|
15
|
+
s.tr('+', ' ').gsub(/((?:%[0-9a-fA-F]{2})+)/n){
|
16
|
+
[$1.delete('%')].pack('H*')
|
17
|
+
}
|
18
|
+
end
|
19
|
+
|
20
|
+
# Parses a query string by breaking it up at the '&'
|
21
|
+
# and ';' characters. You can also use this to parse
|
22
|
+
# cookies by changing the characters used in the second
|
23
|
+
# parameter (which defaults to '&;'.
|
24
|
+
def self.query_parse(qs, d = '&;')
|
25
|
+
params = {}
|
26
|
+
|
27
|
+
qs.split(/[#{d}] */n).each do |p|
|
28
|
+
k, v = unescape(p).split('=', 2)
|
29
|
+
|
30
|
+
if cur = params[k]
|
31
|
+
if cur.kind_of? Array
|
32
|
+
params[k] << v
|
33
|
+
else
|
34
|
+
params[k] = [cur, v]
|
35
|
+
end
|
36
|
+
else
|
37
|
+
params[k] = v
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
return params
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
data/lib/puma_http11.jar
ADDED
Binary file
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'rack/handler'
|
2
|
+
require 'puma'
|
3
|
+
|
4
|
+
module Rack
|
5
|
+
module Handler
|
6
|
+
module Puma
|
7
|
+
DEFAULT_OPTIONS = {
|
8
|
+
:Host => '0.0.0.0',
|
9
|
+
:Port => 8080,
|
10
|
+
:Threads => '0:16',
|
11
|
+
:Quiet => false
|
12
|
+
}
|
13
|
+
|
14
|
+
def self.run(app, options = {})
|
15
|
+
options = DEFAULT_OPTIONS.merge(options)
|
16
|
+
|
17
|
+
unless options[:Quiet]
|
18
|
+
app = Rack::CommonLogger.new(app, STDOUT)
|
19
|
+
end
|
20
|
+
|
21
|
+
server = ::Puma::Server.new(app)
|
22
|
+
min, max = options[:Threads].split(':', 2)
|
23
|
+
|
24
|
+
puts "Puma #{::Puma::Const::PUMA_VERSION} starting..."
|
25
|
+
puts "* Min threads: #{min}, max threads: #{max}"
|
26
|
+
puts "* Listening on tcp://#{options[:Host]}:#{options[:Port]}"
|
27
|
+
|
28
|
+
server.add_tcp_listener options[:Host], options[:Port]
|
29
|
+
server.min_threads = Integer(min)
|
30
|
+
server.max_threads = Integer(max)
|
31
|
+
yield server if block_given?
|
32
|
+
|
33
|
+
server.run.join
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.valid_options
|
37
|
+
{
|
38
|
+
"Host=HOST" => "Hostname to listen on (default: localhost)",
|
39
|
+
"Port=PORT" => "Port to listen on (default: 8080)",
|
40
|
+
"Threads=MIN:MAX" => "min:max threads to use (default 0:16)",
|
41
|
+
"Quiet" => "Don't report each request"
|
42
|
+
}
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
register :puma, Puma
|
47
|
+
end
|
48
|
+
end
|
data/puma.gemspec
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = "puma"
|
5
|
+
s.version = "0.8.2"
|
6
|
+
|
7
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
8
|
+
s.authors = ["Evan Phoenix"]
|
9
|
+
s.date = "2011-11-22"
|
10
|
+
s.description = "Puma is a small library that provides a very fast and concurrent HTTP 1.1 server for Ruby web applications. It is designed for running rack apps only.\n\nWhat makes Puma so fast is the careful use of an Ragel extension to provide fast, accurate HTTP 1.1 protocol parsing. This makes the server scream without too many portability issues."
|
11
|
+
s.email = ["evan@phx.io"]
|
12
|
+
s.executables = ["puma"]
|
13
|
+
s.extensions = ["ext/puma_http11/extconf.rb"]
|
14
|
+
s.extra_rdoc_files = ["History.txt", "Manifest.txt"]
|
15
|
+
s.files = ["COPYING", "Gemfile", "History.txt", "LICENSE", "Manifest.txt", "README.md", "Rakefile", "TODO", "bin/puma", "examples/builder.rb", "examples/camping/README", "examples/camping/blog.rb", "examples/camping/tepee.rb", "examples/httpd.conf", "examples/mime.yaml", "examples/mongrel.conf", "examples/monitrc", "examples/random_thrash.rb", "examples/simpletest.rb", "examples/webrick_compare.rb", "ext/puma_http11/PumaHttp11Service.java", "ext/puma_http11/ext_help.h", "ext/puma_http11/extconf.rb", "ext/puma_http11/http11_parser.c", "ext/puma_http11/http11_parser.h", "ext/puma_http11/http11_parser.java.rl", "ext/puma_http11/http11_parser.rl", "ext/puma_http11/http11_parser_common.rl", "ext/puma_http11/org/jruby/puma/Http11.java", "ext/puma_http11/org/jruby/puma/Http11Parser.java", "ext/puma_http11/puma_http11.c", "lib/puma.rb", "lib/puma/cli.rb", "lib/puma/const.rb", "lib/puma/events.rb", "lib/puma/gems.rb", "lib/puma/mime_types.yml", "lib/puma/rack_patch.rb", "lib/puma/server.rb", "lib/puma/thread_pool.rb", "lib/puma/utils.rb", "lib/rack/handler/puma.rb", "puma.gemspec", "tasks/gem.rake", "tasks/java.rake", "tasks/native.rake", "tasks/ragel.rake", "test/lobster.ru", "test/mime.yaml", "test/test_cli.rb", "test/test_http10.rb", "test/test_http11.rb", "test/test_persistent.rb", "test/test_rack_handler.rb", "test/test_rack_server.rb", "test/test_thread_pool.rb", "test/test_unix_socket.rb", "test/test_ws.rb", "test/testhelp.rb", "tools/trickletest.rb", ".gemtest"]
|
16
|
+
s.rdoc_options = ["--main", "README.md"]
|
17
|
+
s.require_paths = ["lib"]
|
18
|
+
s.rubyforge_project = "puma"
|
19
|
+
s.rubygems_version = "1.8.10"
|
20
|
+
s.summary = "Puma is a small library that provides a very fast and concurrent HTTP 1.1 server for Ruby web applications"
|
21
|
+
s.test_files = ["test/test_cli.rb", "test/test_http10.rb", "test/test_http11.rb", "test/test_persistent.rb", "test/test_rack_handler.rb", "test/test_rack_server.rb", "test/test_thread_pool.rb", "test/test_unix_socket.rb", "test/test_ws.rb"]
|
22
|
+
|
23
|
+
if s.respond_to? :specification_version then
|
24
|
+
s.specification_version = 3
|
25
|
+
|
26
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
27
|
+
s.add_runtime_dependency(%q<rack>, ["~> 1.2"])
|
28
|
+
s.add_development_dependency(%q<rake-compiler>, ["~> 0.7.0"])
|
29
|
+
s.add_development_dependency(%q<hoe>, ["~> 2.10"])
|
30
|
+
else
|
31
|
+
s.add_dependency(%q<rack>, ["~> 1.2"])
|
32
|
+
s.add_dependency(%q<rake-compiler>, ["~> 0.7.0"])
|
33
|
+
s.add_dependency(%q<hoe>, ["~> 2.10"])
|
34
|
+
end
|
35
|
+
else
|
36
|
+
s.add_dependency(%q<rack>, ["~> 1.2"])
|
37
|
+
s.add_dependency(%q<rake-compiler>, ["~> 0.7.0"])
|
38
|
+
s.add_dependency(%q<hoe>, ["~> 2.10"])
|
39
|
+
end
|
40
|
+
end
|
data/tasks/gem.rake
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'hoe'
|
2
|
+
|
3
|
+
HOE = Hoe.spec 'puma' do
|
4
|
+
self.rubyforge_name = 'puma'
|
5
|
+
self.readme_file = "README.md"
|
6
|
+
developer 'Evan Phoenix', 'evan@phx.io'
|
7
|
+
|
8
|
+
spec_extras[:extensions] = ["ext/puma_http11/extconf.rb"]
|
9
|
+
spec_extras[:executables] = ['puma']
|
10
|
+
|
11
|
+
dependency 'rack', '~> 1.2'
|
12
|
+
|
13
|
+
extra_dev_deps << ['rake-compiler', "~> 0.7.0"]
|
14
|
+
|
15
|
+
clean_globs.push('test_*.log', 'log')
|
16
|
+
end
|
17
|
+
|
18
|
+
file "#{HOE.spec.name}.gemspec" => ['Rakefile', 'tasks/gem.rake'] do |t|
|
19
|
+
puts "Generating #{t.name}"
|
20
|
+
File.open(t.name, 'w') { |f| f.puts HOE.spec.to_ruby }
|
21
|
+
end
|
22
|
+
|
23
|
+
desc "Generate or update the standalone gemspec file for the project"
|
24
|
+
task :gemspec => ["#{HOE.spec.name}.gemspec"]
|
data/tasks/java.rake
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
if IS_JRUBY
|
2
|
+
|
3
|
+
require 'rake/javaextensiontask'
|
4
|
+
|
5
|
+
# build http11 java extension
|
6
|
+
Rake::JavaExtensionTask.new('puma_http11', HOE.spec) do |ext|
|
7
|
+
ext.java_compiling do |gs|
|
8
|
+
gs.dependencies.delete gs.dependencies.find { |d| d.name == 'daemons' }
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
data/tasks/native.rake
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
unless IS_JRUBY
|
2
|
+
|
3
|
+
# use rake-compiler for building the extension
|
4
|
+
require 'rake/extensiontask'
|
5
|
+
|
6
|
+
# build http11 C extension
|
7
|
+
Rake::ExtensionTask.new('puma_http11', HOE.spec) do |ext|
|
8
|
+
# define target for extension (supporting fat binaries)
|
9
|
+
if RUBY_PLATFORM =~ /mingw|mswin/ then
|
10
|
+
RUBY_VERSION =~ /(\d+\.\d+)/
|
11
|
+
ext.lib_dir = "lib/#{$1}"
|
12
|
+
elsif ENV['CROSS']
|
13
|
+
# define cross-compilation tasks when not on Windows.
|
14
|
+
ext.cross_compile = true
|
15
|
+
ext.cross_platform = ['i386-mswin32', 'i386-mingw32']
|
16
|
+
|
17
|
+
ext.cross_compiling do |gs|
|
18
|
+
gs.dependencies.delete gs.dependencies.find { |d| d.name == 'daemons' }
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# cleanup versioned library directory
|
23
|
+
CLEAN.include 'lib/{1.8,1.9}'
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
task :ext_clean do
|
28
|
+
sh "rm -rf lib/puma_http11.bundle"
|
29
|
+
sh "rm -rf lib/puma_http11.jar"
|
30
|
+
sh "rm -rf lib/puma_http11.so"
|
31
|
+
end
|
32
|
+
|
33
|
+
# ensure things are built prior testing
|
34
|
+
task :test => [:compile]
|
35
|
+
|
36
|
+
task :clean => :ext_clean
|
data/tasks/ragel.rake
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
|
2
|
+
# the following tasks ease the build of C file from Ragel one
|
3
|
+
|
4
|
+
file 'ext/puma_http11/http11_parser.c' => ['ext/puma_http11/http11_parser.rl'] do |t|
|
5
|
+
begin
|
6
|
+
sh "ragel #{t.prerequisites.last} -C -G2 -o #{t.name}"
|
7
|
+
rescue
|
8
|
+
fail "Could not build wrapper using Ragel (it failed or not installed?)"
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
file 'ext/puma_http11/org/jruby/puma/Http11Parser.java' => ['ext/puma_http11/http11_parser.java.rl'] do |t|
|
13
|
+
begin
|
14
|
+
sh "ragel #{t.prerequisites.last} -J -G2 -o #{t.name}"
|
15
|
+
rescue
|
16
|
+
fail "Could not build wrapper using Ragel (it failed or not installed?)"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
if IS_JRUBY
|
21
|
+
task :ragel => 'ext/puma_http11/org/jruby/puma/Http11Parser.java'
|
22
|
+
else
|
23
|
+
task :ragel => 'ext/puma_http11/http11_parser.c'
|
24
|
+
end
|
data/test/lobster.ru
ADDED
data/test/mime.yaml
ADDED
data/test/test_cli.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'puma/cli'
|
3
|
+
require 'tempfile'
|
4
|
+
|
5
|
+
class TestCLI < Test::Unit::TestCase
|
6
|
+
def setup
|
7
|
+
@pid_file = Tempfile.new("puma-test")
|
8
|
+
@pid_path = @pid_file.path
|
9
|
+
@pid_file.close!
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_pid_file
|
13
|
+
cli = Puma::CLI.new ["--pidfile", @pid_path]
|
14
|
+
cli.parse_options
|
15
|
+
cli.write_pid
|
16
|
+
|
17
|
+
assert_equal File.read(@pid_path).strip.to_i, Process.pid
|
18
|
+
end
|
19
|
+
end
|
data/test/test_http10.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'test/testhelp'
|
2
|
+
|
3
|
+
class Http10ParserTest < Test::Unit::TestCase
|
4
|
+
include Puma
|
5
|
+
|
6
|
+
def test_parse_simple
|
7
|
+
parser = HttpParser.new
|
8
|
+
req = {}
|
9
|
+
http = "GET / HTTP/1.0\r\n\r\n"
|
10
|
+
nread = parser.execute(req, http, 0)
|
11
|
+
|
12
|
+
assert nread == http.length, "Failed to parse the full HTTP request"
|
13
|
+
assert parser.finished?, "Parser didn't finish"
|
14
|
+
assert !parser.error?, "Parser had error"
|
15
|
+
assert nread == parser.nread, "Number read returned from execute does not match"
|
16
|
+
|
17
|
+
assert_equal '/', req['REQUEST_PATH']
|
18
|
+
assert_equal 'HTTP/1.0', req['HTTP_VERSION']
|
19
|
+
assert_equal '/', req['REQUEST_URI']
|
20
|
+
assert_equal 'GET', req['REQUEST_METHOD']
|
21
|
+
assert_nil req['FRAGMENT']
|
22
|
+
assert_nil req['QUERY_STRING']
|
23
|
+
|
24
|
+
parser.reset
|
25
|
+
assert parser.nread == 0, "Number read after reset should be 0"
|
26
|
+
end
|
27
|
+
end
|
data/test/test_http11.rb
ADDED
@@ -0,0 +1,151 @@
|
|
1
|
+
# Copyright (c) 2011 Evan Phoenix
|
2
|
+
# Copyright (c) 2005 Zed A. Shaw
|
3
|
+
|
4
|
+
require 'test/testhelp'
|
5
|
+
|
6
|
+
include Puma
|
7
|
+
|
8
|
+
class Http11ParserTest < Test::Unit::TestCase
|
9
|
+
|
10
|
+
def test_parse_simple
|
11
|
+
parser = HttpParser.new
|
12
|
+
req = {}
|
13
|
+
http = "GET / HTTP/1.1\r\n\r\n"
|
14
|
+
nread = parser.execute(req, http, 0)
|
15
|
+
|
16
|
+
assert nread == http.length, "Failed to parse the full HTTP request"
|
17
|
+
assert parser.finished?, "Parser didn't finish"
|
18
|
+
assert !parser.error?, "Parser had error"
|
19
|
+
assert nread == parser.nread, "Number read returned from execute does not match"
|
20
|
+
|
21
|
+
assert_equal '/', req['REQUEST_PATH']
|
22
|
+
assert_equal 'HTTP/1.1', req['HTTP_VERSION']
|
23
|
+
assert_equal '/', req['REQUEST_URI']
|
24
|
+
assert_equal 'GET', req['REQUEST_METHOD']
|
25
|
+
assert_nil req['FRAGMENT']
|
26
|
+
assert_nil req['QUERY_STRING']
|
27
|
+
|
28
|
+
parser.reset
|
29
|
+
assert parser.nread == 0, "Number read after reset should be 0"
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_parse_dumbfuck_headers
|
33
|
+
parser = HttpParser.new
|
34
|
+
req = {}
|
35
|
+
should_be_good = "GET / HTTP/1.1\r\naaaaaaaaaaaaa:++++++++++\r\n\r\n"
|
36
|
+
nread = parser.execute(req, should_be_good, 0)
|
37
|
+
assert_equal should_be_good.length, nread
|
38
|
+
assert parser.finished?
|
39
|
+
assert !parser.error?
|
40
|
+
|
41
|
+
nasty_pound_header = "GET / HTTP/1.1\r\nX-SSL-Bullshit: -----BEGIN CERTIFICATE-----\r\n\tMIIFbTCCBFWgAwIBAgICH4cwDQYJKoZIhvcNAQEFBQAwcDELMAkGA1UEBhMCVUsx\r\n\tETAPBgNVBAoTCGVTY2llbmNlMRIwEAYDVQQLEwlBdXRob3JpdHkxCzAJBgNVBAMT\r\n\tAkNBMS0wKwYJKoZIhvcNAQkBFh5jYS1vcGVyYXRvckBncmlkLXN1cHBvcnQuYWMu\r\n\tdWswHhcNMDYwNzI3MTQxMzI4WhcNMDcwNzI3MTQxMzI4WjBbMQswCQYDVQQGEwJV\r\n\tSzERMA8GA1UEChMIZVNjaWVuY2UxEzARBgNVBAsTCk1hbmNoZXN0ZXIxCzAJBgNV\r\n\tBAcTmrsogriqMWLAk1DMRcwFQYDVQQDEw5taWNoYWVsIHBhcmQYJKoZIhvcNAQEB\r\n\tBQADggEPADCCAQoCggEBANPEQBgl1IaKdSS1TbhF3hEXSl72G9J+WC/1R64fAcEF\r\n\tW51rEyFYiIeZGx/BVzwXbeBoNUK41OK65sxGuflMo5gLflbwJtHBRIEKAfVVp3YR\r\n\tgW7cMA/s/XKgL1GEC7rQw8lIZT8RApukCGqOVHSi/F1SiFlPDxuDfmdiNzL31+sL\r\n\t0iwHDdNkGjy5pyBSB8Y79dsSJtCW/iaLB0/n8Sj7HgvvZJ7x0fr+RQjYOUUfrePP\r\n\tu2MSpFyf+9BbC/aXgaZuiCvSR+8Snv3xApQY+fULK/xY8h8Ua51iXoQ5jrgu2SqR\r\n\twgA7BUi3G8LFzMBl8FRCDYGUDy7M6QaHXx1ZWIPWNKsCAwEAAaOCAiQwggIgMAwG\r\n\tA1UdEwEB/wQCMAAwEQYJYIZIAYb4QgEBBAQDAgWgMA4GA1UdDwEB/wQEAwID6DAs\r\n\tBglghkgBhvhCAQ0EHxYdVUsgZS1TY2llbmNlIFVzZXIgQ2VydGlmaWNhdGUwHQYD\r\n\tVR0OBBYEFDTt/sf9PeMaZDHkUIldrDYMNTBZMIGaBgNVHSMEgZIwgY+AFAI4qxGj\r\n\tloCLDdMVKwiljjDastqooXSkcjBwMQswCQYDVQQGEwJVSzERMA8GA1UEChMIZVNj\r\n\taWVuY2UxEjAQBgNVBAsTCUF1dGhvcml0eTELMAkGA1UEAxMCQ0ExLTArBgkqhkiG\r\n\t9w0BCQEWHmNhLW9wZXJhdG9yQGdyaWQtc3VwcG9ydC5hYy51a4IBADApBgNVHRIE\r\n\tIjAggR5jYS1vcGVyYXRvckBncmlkLXN1cHBvcnQuYWMudWswGQYDVR0gBBIwEDAO\r\n\tBgwrBgEEAdkvAQEBAQYwPQYJYIZIAYb4QgEEBDAWLmh0dHA6Ly9jYS5ncmlkLXN1\r\n\tcHBvcnQuYWMudmT4sopwqlBWsvcHViL2NybC9jYWNybC5jcmwwPQYJYIZIAYb4QgEDBDAWLmh0\r\n\tdHA6Ly9jYS5ncmlkLXN1cHBvcnQuYWMudWsvcHViL2NybC9jYWNybC5jcmwwPwYD\r\n\tVR0fBDgwNjA0oDKgMIYuaHR0cDovL2NhLmdyaWQt5hYy51ay9wdWIv\r\n\tY3JsL2NhY3JsLmNybDANBgkqhkiG9w0BAQUFAAOCAQEAS/U4iiooBENGW/Hwmmd3\r\n\tXCy6Zrt08YjKCzGNjorT98g8uGsqYjSxv/hmi0qlnlHs+k/3Iobc3LjS5AMYr5L8\r\n\tUO7OSkgFFlLHQyC9JzPfmLCAugvzEbyv4Olnsr8hbxF1MbKZoQxUZtMVu29wjfXk\r\n\thTeApBv7eaKCWpSp7MCbvgzm74izKhu3vlDk9w6qVrxePfGgpKPqfHiOoGhFnbTK\r\n\twTC6o2xq5y0qZ03JonF7OJspEd3I5zKY3E+ov7/ZhW6DqT8UFvsAdjvQbXyhV8Eu\r\n\tYhixw1aKEPzNjNowuIseVogKOLXxWI5vAi5HgXdS0/ES5gDGsABo4fqovUKlgop3\r\n\tRA==\r\n\t-----END CERTIFICATE-----\r\n\r\n"
|
42
|
+
parser = HttpParser.new
|
43
|
+
req = {}
|
44
|
+
#nread = parser.execute(req, nasty_pound_header, 0)
|
45
|
+
#assert_equal nasty_pound_header.length, nread
|
46
|
+
#assert parser.finished?
|
47
|
+
#assert !parser.error?
|
48
|
+
end
|
49
|
+
|
50
|
+
def test_parse_error
|
51
|
+
parser = HttpParser.new
|
52
|
+
req = {}
|
53
|
+
bad_http = "GET / SsUTF/1.1"
|
54
|
+
|
55
|
+
error = false
|
56
|
+
begin
|
57
|
+
nread = parser.execute(req, bad_http, 0)
|
58
|
+
rescue => details
|
59
|
+
error = true
|
60
|
+
end
|
61
|
+
|
62
|
+
assert error, "failed to throw exception"
|
63
|
+
assert !parser.finished?, "Parser shouldn't be finished"
|
64
|
+
assert parser.error?, "Parser SHOULD have error"
|
65
|
+
end
|
66
|
+
|
67
|
+
def test_fragment_in_uri
|
68
|
+
parser = HttpParser.new
|
69
|
+
req = {}
|
70
|
+
get = "GET /forums/1/topics/2375?page=1#posts-17408 HTTP/1.1\r\n\r\n"
|
71
|
+
assert_nothing_raised do
|
72
|
+
parser.execute(req, get, 0)
|
73
|
+
end
|
74
|
+
assert parser.finished?
|
75
|
+
assert_equal '/forums/1/topics/2375?page=1', req['REQUEST_URI']
|
76
|
+
assert_equal 'posts-17408', req['FRAGMENT']
|
77
|
+
end
|
78
|
+
|
79
|
+
# lame random garbage maker
|
80
|
+
def rand_data(min, max, readable=true)
|
81
|
+
count = min + ((rand(max)+1) *10).to_i
|
82
|
+
res = count.to_s + "/"
|
83
|
+
|
84
|
+
if readable
|
85
|
+
res << Digest::SHA1.hexdigest(rand(count * 100).to_s) * (count / 40)
|
86
|
+
else
|
87
|
+
res << Digest::SHA1.digest(rand(count * 100).to_s) * (count / 20)
|
88
|
+
end
|
89
|
+
|
90
|
+
return res
|
91
|
+
end
|
92
|
+
|
93
|
+
|
94
|
+
def test_horrible_queries
|
95
|
+
parser = HttpParser.new
|
96
|
+
|
97
|
+
# then that large header names are caught
|
98
|
+
10.times do |c|
|
99
|
+
get = "GET /#{rand_data(10,120)} HTTP/1.1\r\nX-#{rand_data(1024, 1024+(c*1024))}: Test\r\n\r\n"
|
100
|
+
assert_raises Puma::HttpParserError do
|
101
|
+
parser.execute({}, get, 0)
|
102
|
+
parser.reset
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
# then that large mangled field values are caught
|
107
|
+
10.times do |c|
|
108
|
+
get = "GET /#{rand_data(10,120)} HTTP/1.1\r\nX-Test: #{rand_data(1024, 1024+(c*1024), false)}\r\n\r\n"
|
109
|
+
assert_raises Puma::HttpParserError do
|
110
|
+
parser.execute({}, get, 0)
|
111
|
+
parser.reset
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
# then large headers are rejected too
|
116
|
+
get = "GET /#{rand_data(10,120)} HTTP/1.1\r\n"
|
117
|
+
get << "X-Test: test\r\n" * (80 * 1024)
|
118
|
+
assert_raises Puma::HttpParserError do
|
119
|
+
parser.execute({}, get, 0)
|
120
|
+
parser.reset
|
121
|
+
end
|
122
|
+
|
123
|
+
# finally just that random garbage gets blocked all the time
|
124
|
+
10.times do |c|
|
125
|
+
get = "GET #{rand_data(1024, 1024+(c*1024), false)} #{rand_data(1024, 1024+(c*1024), false)}\r\n\r\n"
|
126
|
+
assert_raises Puma::HttpParserError do
|
127
|
+
parser.execute({}, get, 0)
|
128
|
+
parser.reset
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
end
|
133
|
+
|
134
|
+
|
135
|
+
|
136
|
+
def test_query_parse
|
137
|
+
res = Utils.query_parse("zed=1&frank=#{Utils.escape('&&& ')}")
|
138
|
+
assert res["zed"], "didn't get the request right"
|
139
|
+
assert res["frank"], "no frank"
|
140
|
+
assert_equal "1", res["zed"], "wrong result"
|
141
|
+
assert_equal "&&& ", Utils.unescape(res["frank"]), "wrong result"
|
142
|
+
|
143
|
+
res = Utils.query_parse("zed=1&zed=2&zed=3&frank=11;zed=45")
|
144
|
+
assert res["zed"], "didn't get the request right"
|
145
|
+
assert res["frank"], "no frank"
|
146
|
+
assert_equal 4,res["zed"].length, "wrong number for zed"
|
147
|
+
assert_equal "11",res["frank"], "wrong number for frank"
|
148
|
+
end
|
149
|
+
|
150
|
+
end
|
151
|
+
|