* lib/gserver.rb: removed unmaintained code.
[ruby-core:40313][Feature #5480] * lib/xmlrpc/httpserver.rb: ditto. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@47187 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
parent
90037145dd
commit
e5c9e56893
@ -1,3 +1,9 @@
|
||||
Fri Aug 15 10:13:37 2014 SHIBATA Hiroshi <shibata.hiroshi@gmail.com>
|
||||
|
||||
* lib/gserver.rb: removed unmaintained code.
|
||||
[ruby-core:40313][Feature #5480]
|
||||
* lib/xmlrpc/httpserver.rb: ditto.
|
||||
|
||||
Fri Aug 15 09:22:12 2014 Eric Wong <e@80x24.org>
|
||||
|
||||
* vm_core.h (rb_thread_struct): reorder to pack
|
||||
|
3
NEWS
3
NEWS
@ -144,6 +144,9 @@ with all sufficient information, see the ChangeLog file.
|
||||
* lib/uri
|
||||
* support RFC 3986. [Feature #2542]
|
||||
|
||||
* GServer
|
||||
* GServer is extracted to gserver gem. It's unmaintain code.
|
||||
|
||||
* Logger
|
||||
* Logger::Application is extracted to logger-application gem. It's unmaintain code.
|
||||
|
||||
|
310
lib/gserver.rb
310
lib/gserver.rb
@ -1,310 +0,0 @@
|
||||
#
|
||||
# Copyright (C) 2001 John W. Small All Rights Reserved
|
||||
#
|
||||
# Author:: John W. Small
|
||||
# Documentation:: Gavin Sinclair
|
||||
# Licence:: Ruby License
|
||||
|
||||
require "socket"
|
||||
require "thread"
|
||||
|
||||
#
|
||||
# GServer implements a generic server, featuring thread pool management,
|
||||
# simple logging, and multi-server management. See HttpServer in
|
||||
# <tt>xmlrpc/httpserver.rb</tt> in the Ruby standard library for an example of
|
||||
# GServer in action.
|
||||
#
|
||||
# Any kind of application-level server can be implemented using this class.
|
||||
# It accepts multiple simultaneous connections from clients, up to an optional
|
||||
# maximum number. Several _services_ (i.e. one service per TCP port) can be
|
||||
# run simultaneously, and stopped at any time through the class method
|
||||
# <tt>GServer.stop(port)</tt>. All the threading issues are handled, saving
|
||||
# you the effort. All events are optionally logged, but you can provide your
|
||||
# own event handlers if you wish.
|
||||
#
|
||||
# == Example
|
||||
#
|
||||
# Using GServer is simple. Below we implement a simple time server, run it,
|
||||
# query it, and shut it down. Try this code in +irb+:
|
||||
#
|
||||
# require 'gserver'
|
||||
#
|
||||
# #
|
||||
# # A server that returns the time in seconds since 1970.
|
||||
# #
|
||||
# class TimeServer < GServer
|
||||
# def initialize(port=10001, *args)
|
||||
# super(port, *args)
|
||||
# end
|
||||
# def serve(io)
|
||||
# io.puts(Time.now.to_i)
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# # Run the server with logging enabled (it's a separate thread).
|
||||
# server = TimeServer.new
|
||||
# server.audit = true # Turn logging on.
|
||||
# server.start
|
||||
#
|
||||
# # *** Now point your browser to http://localhost:10001 to see it working ***
|
||||
#
|
||||
# # See if it's still running.
|
||||
# GServer.in_service?(10001) # -> true
|
||||
# server.stopped? # -> false
|
||||
#
|
||||
# # Shut the server down gracefully.
|
||||
# server.shutdown
|
||||
#
|
||||
# # Alternatively, stop it immediately.
|
||||
# GServer.stop(10001)
|
||||
# # or, of course, "server.stop".
|
||||
#
|
||||
# All the business of accepting connections and exception handling is taken
|
||||
# care of. All we have to do is implement the method that actually serves the
|
||||
# client.
|
||||
#
|
||||
# === Advanced
|
||||
#
|
||||
# As the example above shows, the way to use GServer is to subclass it to
|
||||
# create a specific server, overriding the +serve+ method. You can override
|
||||
# other methods as well if you wish, perhaps to collect statistics, or emit
|
||||
# more detailed logging.
|
||||
#
|
||||
# * #connecting
|
||||
# * #disconnecting
|
||||
# * #starting
|
||||
# * #stopping
|
||||
#
|
||||
# The above methods are only called if auditing is enabled, via #audit=.
|
||||
#
|
||||
# You can also override #log and #error if, for example, you wish to use a
|
||||
# more sophisticated logging system.
|
||||
#
|
||||
class GServer
|
||||
|
||||
DEFAULT_HOST = "127.0.0.1"
|
||||
|
||||
def serve(io)
|
||||
end
|
||||
|
||||
@@services = {} # Hash of opened ports, i.e. services
|
||||
@@servicesMutex = Mutex.new
|
||||
|
||||
# Stop the server running on the given port, bound to the given host
|
||||
#
|
||||
# +port+:: port, as a Fixnum, of the server to stop
|
||||
# +host+:: host on which to find the server to stop
|
||||
def GServer.stop(port, host = DEFAULT_HOST)
|
||||
@@servicesMutex.synchronize {
|
||||
@@services[host][port].stop
|
||||
}
|
||||
end
|
||||
|
||||
# Check if a server is running on the given port and host
|
||||
#
|
||||
# +port+:: port, as a Fixnum, of the server to check
|
||||
# +host+:: host on which to find the server to check
|
||||
#
|
||||
# Returns true if a server is running on that port and host.
|
||||
def GServer.in_service?(port, host = DEFAULT_HOST)
|
||||
@@services.has_key?(host) and
|
||||
@@services[host].has_key?(port)
|
||||
end
|
||||
|
||||
# Stop the server
|
||||
def stop
|
||||
@connectionsMutex.synchronize {
|
||||
if @tcpServerThread
|
||||
@tcpServerThread.raise "stop"
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
# Returns true if the server has stopped.
|
||||
def stopped?
|
||||
@tcpServerThread == nil
|
||||
end
|
||||
|
||||
# Schedule a shutdown for the server
|
||||
def shutdown
|
||||
@shutdown = true
|
||||
end
|
||||
|
||||
# Return the current number of connected clients
|
||||
def connections
|
||||
@connections.size
|
||||
end
|
||||
|
||||
# Join with the server thread
|
||||
def join
|
||||
@tcpServerThread.join if @tcpServerThread
|
||||
end
|
||||
|
||||
# Port on which to listen, as a Fixnum
|
||||
attr_reader :port
|
||||
# Host on which to bind, as a String
|
||||
attr_reader :host
|
||||
# Maximum number of connections to accept at a time, as a Fixnum
|
||||
attr_reader :maxConnections
|
||||
# IO Device on which log messages should be written
|
||||
attr_accessor :stdlog
|
||||
# Set to true to cause the callbacks #connecting, #disconnecting, #starting,
|
||||
# and #stopping to be called during the server's lifecycle
|
||||
attr_accessor :audit
|
||||
# Set to true to show more detailed logging
|
||||
attr_accessor :debug
|
||||
|
||||
# Called when a client connects, if auditing is enabled.
|
||||
#
|
||||
# +client+:: a TCPSocket instance representing the client that connected
|
||||
#
|
||||
# Return true to allow this client to connect, false to prevent it.
|
||||
def connecting(client)
|
||||
addr = client.peeraddr
|
||||
log("#{self.class} #{@host}:#{@port} client:#{addr[1]} " +
|
||||
"#{addr[2]}<#{addr[3]}> connect")
|
||||
true
|
||||
end
|
||||
|
||||
|
||||
# Called when a client disconnects, if audition is enabled.
|
||||
#
|
||||
# +clientPort+:: the port of the client that is connecting
|
||||
def disconnecting(clientPort)
|
||||
log("#{self.class} #{@host}:#{@port} " +
|
||||
"client:#{clientPort} disconnect")
|
||||
end
|
||||
|
||||
protected :connecting, :disconnecting
|
||||
|
||||
# Called when the server is starting up, if auditing is enabled.
|
||||
def starting()
|
||||
log("#{self.class} #{@host}:#{@port} start")
|
||||
end
|
||||
|
||||
# Called when the server is shutting down, if auditing is enabled.
|
||||
def stopping()
|
||||
log("#{self.class} #{@host}:#{@port} stop")
|
||||
end
|
||||
|
||||
protected :starting, :stopping
|
||||
|
||||
# Called if #debug is true whenever an unhandled exception is raised.
|
||||
# This implementation simply logs the backtrace.
|
||||
#
|
||||
# +detail+:: the Exception that was caught
|
||||
def error(detail)
|
||||
log(detail.backtrace.join("\n"))
|
||||
end
|
||||
|
||||
# Log a message to #stdlog, if it's defined. This implementation
|
||||
# outputs the timestamp and message to the log.
|
||||
#
|
||||
# +msg+:: the message to log
|
||||
def log(msg)
|
||||
if @stdlog
|
||||
@stdlog.puts("[#{Time.new.ctime}] %s" % msg)
|
||||
@stdlog.flush
|
||||
end
|
||||
end
|
||||
|
||||
protected :error, :log
|
||||
|
||||
# Create a new server
|
||||
#
|
||||
# +port+:: the port, as a Fixnum, on which to listen
|
||||
# +host+:: the host to bind to
|
||||
# +maxConnections+:: the maximum number of simultaneous connections to
|
||||
# accept
|
||||
# +stdlog+:: IO device on which to log messages
|
||||
# +audit+:: if true, lifecycle callbacks will be called. See #audit
|
||||
# +debug+:: if true, error messages are logged. See #debug
|
||||
def initialize(port, host = DEFAULT_HOST, maxConnections = 4,
|
||||
stdlog = $stderr, audit = false, debug = false)
|
||||
@tcpServerThread = nil
|
||||
@port = port
|
||||
@host = host
|
||||
@maxConnections = maxConnections
|
||||
@connections = []
|
||||
@connectionsMutex = Mutex.new
|
||||
@connectionsCV = ConditionVariable.new
|
||||
@stdlog = stdlog
|
||||
@audit = audit
|
||||
@debug = debug
|
||||
end
|
||||
|
||||
# Start the server if it isn't already running
|
||||
#
|
||||
# +maxConnections+::
|
||||
# override +maxConnections+ given to the constructor. A negative
|
||||
# value indicates that the value from the constructor should be used.
|
||||
def start(maxConnections = -1)
|
||||
raise "server is already running" if !stopped?
|
||||
@shutdown = false
|
||||
@maxConnections = maxConnections if maxConnections > 0
|
||||
@@servicesMutex.synchronize {
|
||||
if GServer.in_service?(@port,@host)
|
||||
raise "Port already in use: #{host}:#{@port}!"
|
||||
end
|
||||
@tcpServer = TCPServer.new(@host,@port)
|
||||
@port = @tcpServer.addr[1]
|
||||
@@services[@host] = {} unless @@services.has_key?(@host)
|
||||
@@services[@host][@port] = self;
|
||||
}
|
||||
@tcpServerThread = Thread.new {
|
||||
begin
|
||||
starting if @audit
|
||||
while !@shutdown
|
||||
@connectionsMutex.synchronize {
|
||||
while @connections.size >= @maxConnections
|
||||
@connectionsCV.wait(@connectionsMutex)
|
||||
end
|
||||
}
|
||||
client = @tcpServer.accept
|
||||
Thread.new(client) { |myClient|
|
||||
@connections << Thread.current
|
||||
begin
|
||||
myPort = myClient.peeraddr[1]
|
||||
serve(myClient) if !@audit or connecting(myClient)
|
||||
rescue => detail
|
||||
error(detail) if @debug
|
||||
ensure
|
||||
begin
|
||||
myClient.close
|
||||
rescue
|
||||
end
|
||||
@connectionsMutex.synchronize {
|
||||
@connections.delete(Thread.current)
|
||||
@connectionsCV.signal
|
||||
}
|
||||
disconnecting(myPort) if @audit
|
||||
end
|
||||
}
|
||||
end
|
||||
rescue => detail
|
||||
error(detail) if @debug
|
||||
ensure
|
||||
begin
|
||||
@tcpServer.close
|
||||
rescue
|
||||
end
|
||||
if @shutdown
|
||||
@connectionsMutex.synchronize {
|
||||
while @connections.size > 0
|
||||
@connectionsCV.wait(@connectionsMutex)
|
||||
end
|
||||
}
|
||||
else
|
||||
@connections.each { |c| c.raise "stop" }
|
||||
end
|
||||
@tcpServerThread = nil
|
||||
@@servicesMutex.synchronize {
|
||||
@@services[@host].delete(@port)
|
||||
}
|
||||
stopping if @audit
|
||||
end
|
||||
}
|
||||
self
|
||||
end
|
||||
|
||||
end
|
@ -1,173 +0,0 @@
|
||||
# Copyright (C) 2001, 2002, 2003 by Michael Neumann (mneumann@ntecs.de)
|
||||
#
|
||||
# $Id$
|
||||
#
|
||||
|
||||
|
||||
require "gserver"
|
||||
|
||||
# Implements a simple HTTP-server by using John W. Small's (jsmall@laser.net)
|
||||
# ruby-generic-server: GServer.
|
||||
class HttpServer < GServer
|
||||
|
||||
##
|
||||
# +handle_obj+ specifies the object, that receives calls from +request_handler+
|
||||
# and +ip_auth_handler+
|
||||
def initialize(handle_obj, port = 8080, host = DEFAULT_HOST, maxConnections = 4,
|
||||
stdlog = $stdout, audit = true, debug = true)
|
||||
@handler = handle_obj
|
||||
super(port, host, maxConnections, stdlog, audit, debug)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
CRLF = "\r\n"
|
||||
HTTP_PROTO = "HTTP/1.0"
|
||||
SERVER_NAME = "HttpServer (Ruby #{RUBY_VERSION})"
|
||||
|
||||
# Default header for the server name
|
||||
DEFAULT_HEADER = {
|
||||
"Server" => SERVER_NAME
|
||||
}
|
||||
|
||||
# Mapping of status codes and error messages
|
||||
StatusCodeMapping = {
|
||||
200 => "OK",
|
||||
400 => "Bad Request",
|
||||
403 => "Forbidden",
|
||||
405 => "Method Not Allowed",
|
||||
411 => "Length Required",
|
||||
500 => "Internal Server Error"
|
||||
}
|
||||
|
||||
class Request
|
||||
attr_reader :data, :header, :method, :path, :proto
|
||||
|
||||
def initialize(data, method=nil, path=nil, proto=nil)
|
||||
@header, @data = Table.new, data
|
||||
@method, @path, @proto = method, path, proto
|
||||
end
|
||||
|
||||
def content_length
|
||||
len = @header['Content-Length']
|
||||
return nil if len.nil?
|
||||
return len.to_i
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
class Response
|
||||
attr_reader :header
|
||||
attr_accessor :body, :status, :status_message
|
||||
|
||||
def initialize(status=200)
|
||||
@status = status
|
||||
@status_message = nil
|
||||
@header = Table.new
|
||||
end
|
||||
end
|
||||
|
||||
# A case-insensitive Hash class for HTTP header
|
||||
class Table
|
||||
include Enumerable
|
||||
|
||||
def initialize(hash={})
|
||||
@hash = hash
|
||||
update(hash)
|
||||
end
|
||||
|
||||
def [](key)
|
||||
@hash[key.to_s.capitalize]
|
||||
end
|
||||
|
||||
def []=(key, value)
|
||||
@hash[key.to_s.capitalize] = value
|
||||
end
|
||||
|
||||
def update(hash)
|
||||
hash.each {|k,v| self[k] = v}
|
||||
self
|
||||
end
|
||||
|
||||
def each
|
||||
@hash.each {|k,v| yield k.capitalize, v }
|
||||
end
|
||||
|
||||
# Output the Hash table for the HTTP header
|
||||
def writeTo(port)
|
||||
each { |k,v| port << "#{k}: #{v}" << CRLF }
|
||||
end
|
||||
end # class Table
|
||||
|
||||
|
||||
# Generates a Hash with the HTTP headers
|
||||
def http_header(header=nil) # :doc:
|
||||
new_header = Table.new(DEFAULT_HEADER)
|
||||
new_header.update(header) unless header.nil?
|
||||
|
||||
new_header["Connection"] = "close"
|
||||
new_header["Date"] = http_date(Time.now)
|
||||
|
||||
new_header
|
||||
end
|
||||
|
||||
# Returns a string which represents the time as rfc1123-date of HTTP-date
|
||||
def http_date( aTime ) # :doc:
|
||||
aTime.gmtime.strftime( "%a, %d %b %Y %H:%M:%S GMT" )
|
||||
end
|
||||
|
||||
# Returns a string which includes the status code message as,
|
||||
# http headers, and body for the response.
|
||||
def http_resp(status_code, status_message=nil, header=nil, body=nil) # :doc:
|
||||
status_message ||= StatusCodeMapping[status_code]
|
||||
|
||||
str = ""
|
||||
str << "#{HTTP_PROTO} #{status_code} #{status_message}" << CRLF
|
||||
http_header(header).writeTo(str)
|
||||
str << CRLF
|
||||
str << body unless body.nil?
|
||||
str
|
||||
end
|
||||
|
||||
# Handles the HTTP request and writes the response back to the client, +io+.
|
||||
#
|
||||
# If an Exception is raised while handling the request, the client will receive
|
||||
# a 500 "Internal Server Error" message.
|
||||
def serve(io) # :doc:
|
||||
# perform IP authentication
|
||||
unless @handler.ip_auth_handler(io)
|
||||
io << http_resp(403, "Forbidden")
|
||||
return
|
||||
end
|
||||
|
||||
# parse first line
|
||||
if io.gets =~ /^(\S+)\s+(\S+)\s+(\S+)/
|
||||
request = Request.new(io, $1, $2, $3)
|
||||
else
|
||||
io << http_resp(400, "Bad Request")
|
||||
return
|
||||
end
|
||||
|
||||
# parse HTTP headers
|
||||
while (line=io.gets) !~ /^(\n|\r)/
|
||||
if line =~ /^([\w-]+):\s*(.*)$/
|
||||
request.header[$1] = $2.strip
|
||||
end
|
||||
end
|
||||
|
||||
io.binmode
|
||||
response = Response.new
|
||||
|
||||
# execute request handler
|
||||
@handler.request_handler(request, response)
|
||||
|
||||
# write response back to the client
|
||||
io << http_resp(response.status, response.status_message,
|
||||
response.header, response.body)
|
||||
|
||||
rescue Exception
|
||||
io << http_resp(500, "Internal Server Error")
|
||||
end
|
||||
|
||||
end # class HttpServer
|
||||
|
Loading…
x
Reference in New Issue
Block a user