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:
parent
eb8539af22
commit
f5ecd5dca6
16 changed files with 17 additions and 871 deletions
4
bin/puma
4
bin/puma
|
@ -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'
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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'
|
||||
|
||||
|
|
|
@ -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'
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
@ -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
|
|
@ -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
|
||||
|
|
@ -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'
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue