PageRenderTime 73ms CodeModel.GetById 31ms app.highlight 26ms RepoModel.GetById 14ms app.codeStats 0ms

/tools/Ruby/lib/ruby/1.8/xmlrpc/httpserver.rb

http://github.com/agross/netopenspace
Ruby | 178 lines | 110 code | 42 blank | 26 comment | 4 complexity | 7131bbc08b2e57aae75fc9ceca1b873e MD5 | raw file
  1#
  2# Implements a simple HTTP-server by using John W. Small's (jsmall@laser.net) 
  3# ruby-generic-server.
  4# 
  5# Copyright (C) 2001, 2002, 2003 by Michael Neumann (mneumann@ntecs.de)
  6#
  7# $Id: httpserver.rb 11708 2007-02-12 23:01:19Z shyouhei $
  8#
  9
 10
 11require "gserver"
 12
 13class HttpServer < GServer
 14
 15  ##
 16  # handle_obj specifies the object, that receives calls to request_handler
 17  # and ip_auth_handler 
 18  def initialize(handle_obj, port = 8080, host = DEFAULT_HOST, maxConnections = 4, 
 19                 stdlog = $stdout, audit = true, debug = true)
 20    @handler = handle_obj
 21    super(port, host, maxConnections, stdlog, audit, debug)
 22  end
 23
 24private
 25
 26  # Constants -----------------------------------------------
 27  
 28  CRLF        = "\r\n"
 29  HTTP_PROTO  = "HTTP/1.0"
 30  SERVER_NAME = "HttpServer (Ruby #{RUBY_VERSION})"
 31
 32  DEFAULT_HEADER = {
 33    "Server" => SERVER_NAME
 34  }
 35
 36  ##
 37  # Mapping of status code and error message
 38  #
 39  StatusCodeMapping = {
 40    200 => "OK",
 41    400 => "Bad Request",
 42    403 => "Forbidden",
 43    405 => "Method Not Allowed",
 44    411 => "Length Required",
 45    500 => "Internal Server Error"
 46  }
 47
 48  # Classes -------------------------------------------------
 49  
 50  class Request
 51    attr_reader :data, :header, :method, :path, :proto
 52    
 53    def initialize(data, method=nil, path=nil, proto=nil)
 54      @header, @data = Table.new, data
 55      @method, @path, @proto = method, path, proto
 56    end
 57    
 58    def content_length
 59      len = @header['Content-Length']
 60      return nil if len.nil?
 61      return len.to_i 
 62    end
 63    
 64  end
 65  
 66  class Response
 67    attr_reader   :header
 68    attr_accessor :body, :status, :status_message
 69    
 70    def initialize(status=200)
 71      @status = status
 72      @status_message = nil
 73      @header = Table.new
 74    end
 75  end
 76
 77
 78  ##
 79  # a case-insensitive Hash class for HTTP header
 80  #
 81  class Table
 82    include Enumerable
 83
 84    def initialize(hash={})
 85      @hash = hash 
 86      update(hash)
 87    end
 88
 89    def [](key)
 90      @hash[key.to_s.capitalize]
 91    end
 92
 93    def []=(key, value)
 94      @hash[key.to_s.capitalize] = value
 95    end
 96
 97    def update(hash)
 98      hash.each {|k,v| self[k] = v}
 99      self
100    end
101
102    def each
103      @hash.each {|k,v| yield k.capitalize, v }
104    end
105
106    def writeTo(port)
107      each { |k,v| port << "#{k}: #{v}" << CRLF }
108    end
109  end # class Table
110
111
112  # Helper Methods ------------------------------------------
113
114  def http_header(header=nil)
115    new_header = Table.new(DEFAULT_HEADER)
116    new_header.update(header) unless header.nil? 
117
118    new_header["Connection"] = "close"
119    new_header["Date"]       = http_date(Time.now)
120
121    new_header
122  end
123
124  def http_date( aTime )
125    aTime.gmtime.strftime( "%a, %d %b %Y %H:%M:%S GMT" )
126  end
127
128  def http_resp(status_code, status_message=nil, header=nil, body=nil)
129    status_message ||= StatusCodeMapping[status_code]
130    
131    str = ""
132    str << "#{HTTP_PROTO} #{status_code} #{status_message}" << CRLF
133    http_header(header).writeTo(str)
134    str << CRLF
135    str << body unless body.nil?
136    str
137  end
138
139  # Main Serve Loop -----------------------------------------
140  
141  def serve(io)  
142    # perform IP authentification
143    unless @handler.ip_auth_handler(io)
144      io << http_resp(403, "Forbidden")
145      return
146    end
147
148    # parse first line
149    if io.gets =~ /^(\S+)\s+(\S+)\s+(\S+)/
150      request = Request.new(io, $1, $2, $3)
151    else
152      io << http_resp(400, "Bad Request") 
153      return
154    end
155     
156    # parse HTTP headers
157    while (line=io.gets) !~ /^(\n|\r)/
158      if line =~ /^([\w-]+):\s*(.*)$/
159	request.header[$1] = $2.strip
160      end
161    end
162
163    io.binmode    
164    response = Response.new
165
166    # execute request handler
167    @handler.request_handler(request, response)
168   
169    # write response back to the client
170    io << http_resp(response.status, response.status_message,
171                    response.header, response.body) 
172
173  rescue Exception => e
174    io << http_resp(500, "Internal Server Error")
175  end
176
177end # class HttpServer
178