1
0
Fork 0
mirror of https://github.com/puma/puma.git synced 2022-11-09 13:48:40 -05:00

More cleanup and removal

This commit is contained in:
Evan Phoenix 2011-09-23 20:46:33 -07:00
parent eb8539af22
commit f5ecd5dca6
16 changed files with 17 additions and 871 deletions

View file

@ -1,10 +1,8 @@
#!/usr/bin/env ruby
#
# Copyright (c) 2011 Evan Phoenix
# Copyright (c) 2005 Zed A. Shaw
# You can redistribute it and/or modify it under the same terms as Ruby.
#
# Additional work donated by contributors. See http://puma.rubyforge.org/attributions.html
# for more information.
require 'yaml'
require 'etc'

View file

@ -23,13 +23,9 @@ require 'thread'
# Ruby Puma
require 'puma/command'
require 'puma/tcphack'
require 'puma/configurator'
require 'puma/uri_classifier'
require 'puma/const'
require 'puma/http_request'
require 'puma/header_out'
require 'puma/http_response'
require 'puma/server'
# Puma module containing all of the classes (include C extensions)

View file

@ -1,6 +1,5 @@
# Copyright (c) 2005 Zed A. Shaw
# Copyright (c) 2011 Evan Phoenix
# You can redistribute it and/or modify it under the same terms as Ruby.
# Copyright (c) 2005 Zed A. Shaw
#
require 'singleton'
@ -93,7 +92,7 @@ module Puma
# Validates the given expression is true and prints the message if not, exiting.
def valid?(exp, message)
if not @done_validating and (not exp)
if !@done_validating and !exp
failure message
@valid = false
@done_validating = true
@ -215,12 +214,12 @@ module Puma
command = GemPlugin::Manager.instance.create("/commands/#{cmd_name}", opts)
rescue OptionParser::InvalidOption
@stderr.puts "#$! for command '#{cmd_name}'"
rescue OptionParser::InvalidOption => e
@stderr.puts "#{e} for command '#{cmd_name}'"
@stderr.puts "Try #{cmd_name} -h to get help."
return false
rescue
@stderr.puts "ERROR RUNNING '#{cmd_name}': #$!"
rescue => e
@stderr.puts "ERROR RUNNING '#{cmd_name}': #{e.message} (#{e.class})"
@stderr.puts "Use help command to get help"
return false
end
@ -228,12 +227,12 @@ module Puma
# Normally the command is NOT valid right after being created
# but sometimes (like with -h or -v) there's no further processing
# needed so the command is already valid so we can skip it.
if not command.done_validating
if not command.validate
@stderr.puts "#{cmd_name} reported an error. Use puma_rails #{cmd_name} -h to get help."
return false
else
unless command.done_validating
if command.validate
command.run
else
@stderr.puts "#{cmd_name} reported an error. Use puma #{cmd_name} -h to get help."
return false
end
end

View file

@ -1,40 +0,0 @@
module Puma
# This class implements a simple way of constructing the HTTP headers
# dynamically via a Hash syntax. Think of it as a write-only Hash.
# Refer to HttpResponse for information on how this is used.
#
# One consequence of this write-only nature is that you can write multiple
# headers by just doing them twice (which is sometimes needed in HTTP),
# but that the normal semantics for Hash (where doing an insert replaces)
# is not there.
class HeaderOut
attr_reader :out
attr_accessor :allowed_duplicates
def initialize(out)
@sent = {}
@allowed_duplicates = {
"Set-Cookie" => true,
"Set-Cookie2" => true,
"Warning" => true,
"WWW-Authenticate" => true
}
@out = out
end
# Simply writes "#{key}: #{value}" to an output buffer.
def []=(key,value)
if !@sent.has_key?(key) or @allowed_duplicates.has_key?(key)
@sent[key] = true
o = @out
o.write key
o.write ": "
o.write value
o.write "\r\n"
end
end
end
end

View file

@ -1,165 +0,0 @@
module Puma
# Writes and controls your response to the client using the HTTP/1.1 specification.
# You use it by simply doing:
#
# response.start(200) do |head,out|
# head['Content-Type'] = 'text/plain'
# out.write("hello\n")
# end
#
# The parameter to start is the response code--which Puma will translate for you
# based on HTTP_STATUS_CODES. The head parameter is how you write custom headers.
# The out parameter is where you write your body. The default status code for
# HttpResponse.start is 200 so the above example is redundant.
#
# As you can see, it's just like using a Hash and as you do this it writes the proper
# header to the output on the fly. You can even intermix specifying headers and
# writing content. The HttpResponse class with write the things in the proper order
# once the HttpResponse.block is ended.
#
# You may also work the HttpResponse object directly using the various attributes available
# for the raw socket, body, header, and status codes. If you do this you're on your own.
# A design decision was made to force the client to not pipeline requests. HTTP/1.1
# pipelining really kills the performance due to how it has to be handled and how
# unclear the standard is. To fix this the HttpResponse gives a "Connection: close"
# header which forces the client to close right away. The bonus for this is that it
# gives a pretty nice speed boost to most clients since they can close their connection
# immediately.
#
# One additional caveat is that you don't have to specify the Content-length header
# as the HttpResponse will write this for you based on the out length.
class HttpResponse
attr_reader :socket
attr_reader :body
attr_writer :body
attr_reader :header
attr_reader :status
attr_writer :status
attr_reader :body_sent
attr_reader :header_sent
attr_reader :status_sent
def initialize(socket)
@socket = socket
@body = StringIO.new
@status = 404
@reason = nil
@header = HeaderOut.new(StringIO.new)
@header[Const::DATE] = Time.now.httpdate
@body_sent = false
@header_sent = false
@status_sent = false
end
# Receives a block passing it the header and body for you to work with.
# When the block is finished it writes everything you've done to
# the socket in the proper order. This lets you intermix header and
# body content as needed. Handlers are able to modify pretty much
# any part of the request in the chain, and can stop further processing
# by simple passing "finalize=true" to the start method. By default
# all handlers run and then puma finalizes the request when they're
# all done.
def start(status=200, finalize=false, reason=nil)
@status = status.to_i
@reason = reason
yield @header, @body
finished if finalize
end
# Primarily used in exception handling to reset the response output in order to write
# an alternative response. It will abort with an exception if you have already
# sent the header or the body. This is pretty catastrophic actually.
def reset
if @body_sent
raise "You have already sent the request body."
elsif @header_sent
raise "You have already sent the request headers."
else
@header.out.close
@header = HeaderOut.new(StringIO.new)
@body.close
@body = StringIO.new
end
end
def send_status(content_length=@body.length)
unless @status_sent
@header['Content-Length'] = content_length if content_length and @status != 304
write(Const::STATUS_FORMAT % [@status, @reason || HTTP_STATUS_CODES[@status]])
@status_sent = true
end
end
def send_header
unless @header_sent
@header.out.rewind
write(@header.out.read + Const::LINE_END)
@header_sent = true
end
end
def send_body
unless @body_sent
@body.rewind
write(@body.read)
@body_sent = true
end
end
# Appends the contents of +path+ to the response stream. The file is opened for binary
# reading and written in chunks to the socket.
#
# Sendfile API support has been removed in 0.3.13.4 due to stability problems.
def send_file(path, small_file = false)
if small_file
File.open(path, "rb") {|f| @socket << f.read }
else
File.open(path, "rb") do |f|
while chunk = f.read(Const::CHUNK_SIZE) and chunk.length > 0
begin
write(chunk)
rescue Object => exc
break
end
end
end
end
@body_sent = true
end
def socket_error(details)
# ignore these since it means the client closed off early
@socket.close rescue nil
done = true
raise details
end
def write(data)
@socket.write(data)
rescue => details
socket_error(details)
end
# This takes whatever has been done to header and body and then writes it in the
# proper format to make an HTTP/1.1 response.
def finished
send_status
send_header
send_body
end
# Used during error conditions to mark the response as "done" so there isn't any more processing
# sent to the client.
def done=(val)
@status_sent = true
@header_sent = true
@body_sent = true
end
def done
(@status_sent and @header_sent and @body_sent)
end
end
end

View file

@ -1,8 +0,0 @@
# Copyright (c) 2005 Zed A. Shaw
# You can redistribute it and/or modify it under the same terms as Ruby.
#
require 'puma/gems'
Puma::Gems.require 'puma/gem_plugin'
# File is just a stub that makes sure the puma_plugins gem is loaded and ready

View file

@ -1,86 +0,0 @@
# Copyright (c) 2005 Zed A. Shaw
# You can redistribute it and/or modify it under the same terms as Ruby.
#
# A very simple little class for doing some basic fast statistics sampling.
# You feed it either samples of numeric data you want measured or you call
# Stats.tick to get it to add a time delta between the last time you called it.
# When you're done either call sum, sumsq, n, min, max, mean or sd to get
# the information. The other option is to just call dump and see everything.
#
# It does all of this very fast and doesn't take up any memory since the samples
# are not stored but instead all the values are calculated on the fly.
module Puma
class Stats
attr_reader :sum, :sumsq, :n, :min, :max
def initialize(name)
@name = name
reset
end
# Resets the internal counters so you can start sampling again.
def reset
@sum = 0.0
@sumsq = 0.0
@last_time = Time.new
@n = 0.0
@min = 0.0
@max = 0.0
end
# Adds a sampling to the calculations.
def sample(s)
@sum += s
@sumsq += s * s
if @n == 0
@min = @max = s
else
@min = s if @min > s
@max = s if @max < s
end
@n+=1
end
# Dump this Stats object with an optional additional message.
def dump(msg = "", out=STDERR)
out.puts "#{msg}: #{self.to_s}"
end
# Returns a common display (used by dump)
def to_s
"[#{@name}]: SUM=%0.4f, SUMSQ=%0.4f, N=%0.4f, MEAN=%0.4f, SD=%0.4f, MIN=%0.4f, MAX=%0.4f" % [@sum, @sumsq, @n, mean, sd, @min, @max]
end
# Calculates and returns the mean for the data passed so far.
def mean
@sum / @n
end
# Calculates the standard deviation of the data so far.
def sd
# (sqrt( ((s).sumsq - ( (s).sum * (s).sum / (s).n)) / ((s).n-1) ))
begin
return Math.sqrt( (@sumsq - ( @sum * @sum / @n)) / (@n-1) )
rescue Errno::EDOM
return 0.0
end
end
# Adds a time delta between now and the last time you called this. This
# will give you the average time between two activities.
#
# An example is:
#
# t = Stats.new("do_stuff")
# 10000.times { do_stuff(); t.tick }
# t.dump("time")
#
def tick
now = Time.now
sample(now - @last_time)
@last_time = now
end
end
end

View file

@ -1,16 +0,0 @@
# Copyright (c) 2005 Zed A. Shaw
# You can redistribute it and/or modify it under the same terms as Ruby.
#
# A modification proposed by Sean Treadway that increases the default accept
# queue of TCPServer to 1024 so that it handles more concurrent requests.
class TCPServer
def initialize_with_backlog(*args)
initialize_without_backlog(*args)
listen(1024)
end
alias_method :initialize_without_backlog, :initialize
alias_method :initialize, :initialize_with_backlog
end

View file

@ -1,80 +0,0 @@
module Puma
class URIClassifier
class RegistrationError < RuntimeError
end
class UsageError < RuntimeError
end
attr_reader :handler_map
# Returns the URIs that have been registered with this classifier so far.
def uris
@handler_map.keys
end
def initialize
@handler_map = {}
@matcher = //
@root_handler = nil
end
# Register a handler object at a particular URI. The handler can be whatever
# you want, including an array. It's up to you what to do with it.
#
# Registering a handler is not necessarily threadsafe, so be careful if you go
# mucking around once the server is running.
def register(uri, handler)
raise RegistrationError, "#{uri.inspect} is already registered" if @handler_map[uri]
raise RegistrationError, "URI is empty" if !uri or uri.empty?
raise RegistrationError, "URI must begin with a \"#{Const::SLASH}\"" unless uri[0..0] == Const::SLASH
@handler_map[uri.dup] = handler
rebuild
end
# Unregister a particular URI and its handler.
def unregister(uri)
handler = @handler_map.delete(uri)
raise RegistrationError, "#{uri.inspect} was not registered" unless handler
rebuild
handler
end
# Resolve a request URI by finding the best partial match in the registered
# handler URIs.
def resolve(request_uri)
if @root_handler
# Optimization for the pathological case of only one handler on "/"; e.g. Rails
[Const::SLASH, request_uri, @root_handler]
elsif match = @matcher.match(request_uri)
uri = match.to_s
# A root mounted ("/") handler must resolve such that path info matches the original URI.
[uri, (uri == Const::SLASH ? request_uri : match.post_match), @handler_map[uri]]
else
[nil, nil, nil]
end
end
def rebuild
if @handler_map.size == 1 and @handler_map[Const::SLASH]
@root_handler = @handler_map.values.first
else
@root_handler = nil
routes = @handler_map.keys.sort.sort_by do |uri|
-uri.length
end
all_possibles = routes.map do |uri|
Regexp.new('^' + Regexp.escape(uri))
end.join('|')
@matcher = Regexp.new(all_possibles)
end
end
private :rebuild
end
end

View file

@ -1,8 +1,5 @@
# Copyright (c) 2011 Evan Phoenix
# Copyright (c) 2005 Zed A. Shaw
# You can redistribute it and/or modify it under the same terms as Ruby.
#
# Additional work donated by contributors. See http://mongrel.rubyforge.org/attributions.html
# for more information.
require 'test/testhelp'

View file

@ -1,8 +1,5 @@
# Copyright (c) 2011 Evan Phoenix
# Copyright (c) 2005 Zed A. Shaw
# You can redistribute it and/or modify it under the same terms as Ruby.
#
# Additional work donated by contributors. See http://mongrel.rubyforge.org/attributions.html
# for more information.
require 'test/testhelp'

View file

@ -1,127 +0,0 @@
# Copyright (c) 2005 Zed A. Shaw
# You can redistribute it and/or modify it under the same terms as Ruby.
#
# Additional work donated by contributors. See http://mongrel.rubyforge.org/attributions.html
# for more information.
require 'test/testhelp'
include Puma
class ResponseTest < Test::Unit::TestCase
def test_response_headers
out = StringIO.new
resp = HttpResponse.new(out)
resp.status = 200
resp.header["Accept"] = "text/plain"
resp.header["X-Whatever"] = "stuff"
resp.body.write("test")
resp.finished
assert out.length > 0, "output didn't have data"
end
def test_response_200
io = StringIO.new
resp = HttpResponse.new(io)
resp.start do |head,out|
head["Accept"] = "text/plain"
out.write("tested")
out.write("hello!")
end
resp.finished
assert io.length > 0, "output didn't have data"
end
def test_response_duplicate_header_squash
io = StringIO.new
resp = HttpResponse.new(io)
resp.start do |head,out|
head["Content-Length"] = 30
head["Content-Length"] = 0
end
resp.finished
assert_equal io.length, 95, "too much output"
end
def test_response_some_duplicates_allowed
allowed_duplicates = ["Set-Cookie", "Set-Cookie2", "Warning", "WWW-Authenticate"]
io = StringIO.new
resp = HttpResponse.new(io)
resp.start do |head,out|
allowed_duplicates.each do |dup|
10.times do |i|
head[dup] = i
end
end
end
resp.finished
assert_equal io.length, 734, "wrong amount of output"
end
def test_response_404
io = StringIO.new
resp = HttpResponse.new(io)
resp.start(404) do |head,out|
head['Accept'] = "text/plain"
out.write("NOT FOUND")
end
resp.finished
assert io.length > 0, "output didn't have data"
end
def test_response_file
contents = "PLAIN TEXT\r\nCONTENTS\r\n"
require 'tempfile'
tmpf = Tempfile.new("test_response_file")
tmpf.binmode
tmpf.write(contents)
tmpf.rewind
io = StringIO.new
resp = HttpResponse.new(io)
resp.start(200) do |head,out|
head['Content-Type'] = 'text/plain'
resp.send_header
resp.send_file(tmpf.path)
end
io.rewind
tmpf.close
assert io.length > 0, "output didn't have data"
assert io.read[-contents.length..-1] == contents, "output doesn't end with file payload"
end
def test_response_with_custom_reason
reason = "You made a bad request"
io = StringIO.new
resp = HttpResponse.new(io)
resp.start(400, false, reason) { |head,out| }
resp.finished
io.rewind
assert_match(/.* #{reason}$/, io.readline.chomp, "wrong custom reason phrase")
end
def test_response_with_default_reason
code = 400
io = StringIO.new
resp = HttpResponse.new(io)
resp.start(code) { |head,out| }
resp.finished
io.rewind
assert_match(/.* #{HTTP_STATUS_CODES[code]}$/, io.readline.chomp, "wrong default reason phrase")
end
end

View file

@ -1,35 +0,0 @@
# Copyright (c) 2005 Zed A. Shaw
# You can redistribute it and/or modify it under the same terms as Ruby.
#
# Additional work donated by contributors. See http://mongrel.rubyforge.org/attributions.html
# for more information.
require 'test/testhelp'
class StatsTest < Test::Unit::TestCase
def test_sampling_speed
out = StringIO.new
s = Puma::Stats.new("test")
t = Puma::Stats.new("time")
100.times { s.sample(rand(20)); t.tick }
s.dump("FIRST", out)
t.dump("FIRST", out)
old_mean = s.mean
old_sd = s.sd
s.reset
t.reset
100.times { s.sample(rand(30)); t.tick }
s.dump("SECOND", out)
t.dump("SECOND", out)
assert_not_equal old_mean, s.mean
assert_not_equal old_mean, s.sd
end
end

View file

@ -1,261 +0,0 @@
# Copyright (c) 2005 Zed A. Shaw
# You can redistribute it and/or modify it under the same terms as Ruby.
#
# Additional work donated by contributors. See http://mongrel.rubyforge.org/attributions.html
# for more information.
require 'test/testhelp'
include Puma
class URIClassifierTest < Test::Unit::TestCase
def test_uri_finding
uri_classifier = URIClassifier.new
uri_classifier.register("/test", 1)
script_name, path_info, value = uri_classifier.resolve("/test")
assert_equal 1, value
assert_equal "/test", script_name
end
def test_root_handler_only
uri_classifier = URIClassifier.new
uri_classifier.register("/", 1)
script_name, path_info, value = uri_classifier.resolve("/test")
assert_equal 1, value
assert_equal "/", script_name
assert_equal "/test", path_info
end
def test_uri_prefix_ops
test = "/pre/fix/test"
prefix = "/pre"
uri_classifier = URIClassifier.new
uri_classifier.register(prefix,1)
script_name, path_info, value = uri_classifier.resolve(prefix)
script_name, path_info, value = uri_classifier.resolve(test)
assert_equal 1, value
assert_equal prefix, script_name
assert_equal test[script_name.length .. -1], path_info
assert uri_classifier.inspect
assert_equal prefix, uri_classifier.uris[0]
end
def test_not_finding
test = "/cant/find/me"
uri_classifier = URIClassifier.new
uri_classifier.register(test, 1)
script_name, path_info, value = uri_classifier.resolve("/nope/not/here")
assert_nil script_name
assert_nil path_info
assert_nil value
end
def test_exceptions
uri_classifier = URIClassifier.new
uri_classifier.register("/test", 1)
failed = false
begin
uri_classifier.register("/test", 1)
rescue => e
failed = true
end
assert failed
failed = false
begin
uri_classifier.register("", 1)
rescue => e
failed = true
end
assert failed
end
def test_register_unregister
uri_classifier = URIClassifier.new
100.times do
uri_classifier.register("/stuff", 1)
value = uri_classifier.unregister("/stuff")
assert_equal 1, value
end
uri_classifier.register("/things",1)
script_name, path_info, value = uri_classifier.resolve("/things")
assert_equal 1, value
uri_classifier.unregister("/things")
script_name, path_info, value = uri_classifier.resolve("/things")
assert_nil value
end
def test_uri_branching
uri_classifier = URIClassifier.new
uri_classifier.register("/test", 1)
uri_classifier.register("/test/this",2)
script_name, path_info, handler = uri_classifier.resolve("/test")
script_name, path_info, handler = uri_classifier.resolve("/test/that")
assert_equal "/test", script_name, "failed to properly find script off branch portion of uri"
assert_equal "/that", path_info
assert_equal 1, handler, "wrong result for branching uri"
end
def test_all_prefixing
tests = ["/test","/test/that","/test/this"]
uri = "/test/this/that"
uri_classifier = URIClassifier.new
current = ""
uri.each_byte do |c|
current << c.chr
uri_classifier.register(current, c)
end
# Try to resolve everything with no asserts as a fuzzing
tests.each do |prefix|
current = ""
prefix.each_byte do |c|
current << c.chr
script_name, path_info, handler = uri_classifier.resolve(current)
assert script_name
assert path_info
assert handler
end
end
# Assert that we find stuff
tests.each do |t|
script_name, path_info, handler = uri_classifier.resolve(t)
assert handler
end
# Assert we don't find stuff
script_name, path_info, handler = uri_classifier.resolve("chicken")
assert_nil handler
assert_nil script_name
assert_nil path_info
end
# Verifies that a root mounted ("/") handler resolves
# such that path info matches the original URI.
# This is needed to accommodate real usage of handlers.
def test_root_mounted
uri_classifier = URIClassifier.new
root = "/"
path = "/this/is/a/test"
uri_classifier.register(root, 1)
script_name, path_info, handler = uri_classifier.resolve(root)
assert_equal 1, handler
assert_equal root, path_info
assert_equal root, script_name
script_name, path_info, handler = uri_classifier.resolve(path)
assert_equal path, path_info
assert_equal root, script_name
assert_equal 1, handler
end
# Verifies that a root mounted ("/") handler
# is the default point, doesn't matter the order we use
# to register the URIs
def test_classifier_order
tests = ["/before", "/way_past"]
root = "/"
path = "/path"
uri_classifier = URIClassifier.new
uri_classifier.register(path, 1)
uri_classifier.register(root, 2)
tests.each do |uri|
script_name, path_info, handler = uri_classifier.resolve(uri)
assert_equal root, script_name, "#{uri} did not resolve to #{root}"
assert_equal uri, path_info
assert_equal 2, handler
end
end
if ENV['BENCHMARK']
# Eventually we will have a suite of benchmarks instead of lamely installing a test
def test_benchmark
# This URI set should favor a TST. Both versions increase linearly until you hit 14
# URIs, then the TST flattens out.
@uris = %w(
/
/dag /dig /digbark /dog /dogbark /dog/bark /dug /dugbarking /puppy
/c /cat /cat/tree /cat/tree/mulberry /cats /cot /cot/tree/mulberry /kitty /kittycat
# /eag /eig /eigbark /eog /eogbark /eog/bark /eug /eugbarking /iuppy
# /f /fat /fat/tree /fat/tree/mulberry /fats /fot /fot/tree/mulberry /jitty /jittyfat
# /gag /gig /gigbark /gog /gogbark /gog/bark /gug /gugbarking /kuppy
# /h /hat /hat/tree /hat/tree/mulberry /hats /hot /hot/tree/mulberry /litty /littyhat
# /ceag /ceig /ceigbark /ceog /ceogbark /ceog/cbark /ceug /ceugbarking /ciuppy
# /cf /cfat /cfat/ctree /cfat/ctree/cmulberry /cfats /cfot /cfot/ctree/cmulberry /cjitty /cjittyfat
# /cgag /cgig /cgigbark /cgog /cgogbark /cgog/cbark /cgug /cgugbarking /ckuppy
# /ch /chat /chat/ctree /chat/ctree/cmulberry /chats /chot /chot/ctree/cmulberry /citty /cittyhat
)
@requests = %w(
/
/dig
/digging
/dogging
/dogbarking/
/puppy/barking
/c
/cat
/cat/shrub
/cat/tree
/cat/tree/maple
/cat/tree/mulberry/tree
/cat/tree/oak
/cats/
/cats/tree
/cod
/zebra
)
@classifier = URIClassifier.new
@uris.each do |uri|
@classifier.register(uri, 1)
end
puts "#{@uris.size} URIs / #{@requests.size * 10000} requests"
Benchmark.bm do |x|
x.report do
# require 'ruby-prof'
# profile = RubyProf.profile do
10000.times do
@requests.each do |request|
@classifier.resolve(request)
end
end
# end
# File.open("profile.html", 'w') { |file| RubyProf::GraphHtmlPrinter.new(profile).print(file, 0) }
end
end
end
end
end

View file

@ -1,8 +1,5 @@
# Copyright (c) 2011 Evan Phoenix
# Copyright (c) 2005 Zed A. Shaw
# You can redistribute it and/or modify it under the same terms as Ruby.
#
# Additional work donated by contributors. See http://mongrel.rubyforge.org/attributions.html
# for more information.
require 'test/testhelp'

View file

@ -1,34 +1,19 @@
# Copyright (c) 2011 Evan Phoenix
# Copyright (c) 2005 Zed A. Shaw
# You can redistribute it and/or modify it under the same terms as Ruby.
#
# Additional work donated by contributors. See http://puma.rubyforge.org/attributions.html
# for more information.
HERE = File.dirname(__FILE__)
%w(lib ext bin test).each do |dir|
$LOAD_PATH.unshift "#{HERE}/../#{dir}"
$LOAD_PATH.unshift File.expand_path("../../#{dir}", __FILE__)
end
require 'rubygems'
require 'test/unit'
require 'net/http'
require 'timeout'
require 'cgi/session'
require 'fileutils'
require 'benchmark'
require 'digest/sha1'
require 'uri'
require 'stringio'
require 'pp'
require 'puma'
require 'puma/stats'
if ENV['DEBUG']
require 'ruby-debug'
Debugger.start
end
def redirect_test_io
yield
@ -54,8 +39,3 @@ def hit(uris)
return results
end
# Platform check helper ;-)
def windows?
@windows ||= RbConfig::CONFIG['host_os'] =~ /mingw|mswin/
end