/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. require "gserver"
  10. class HttpServer < GServer
  11. ##
  12. # handle_obj specifies the object, that receives calls to request_handler
  13. # and ip_auth_handler
  14. def initialize(handle_obj, port = 8080, host = DEFAULT_HOST, maxConnections = 4,
  15. stdlog = $stdout, audit = true, debug = true)
  16. @handler = handle_obj
  17. super(port, host, maxConnections, stdlog, audit, debug)
  18. end
  19. private
  20. # Constants -----------------------------------------------
  21. CRLF = "\r\n"
  22. HTTP_PROTO = "HTTP/1.0"
  23. SERVER_NAME = "HttpServer (Ruby #{RUBY_VERSION})"
  24. DEFAULT_HEADER = {
  25. "Server" => SERVER_NAME
  26. }
  27. ##
  28. # Mapping of status code and error message
  29. #
  30. StatusCodeMapping = {
  31. 200 => "OK",
  32. 400 => "Bad Request",
  33. 403 => "Forbidden",
  34. 405 => "Method Not Allowed",
  35. 411 => "Length Required",
  36. 500 => "Internal Server Error"
  37. }
  38. # Classes -------------------------------------------------
  39. class Request
  40. attr_reader :data, :header, :method, :path, :proto
  41. def initialize(data, method=nil, path=nil, proto=nil)
  42. @header, @data = Table.new, data
  43. @method, @path, @proto = method, path, proto
  44. end
  45. def content_length
  46. len = @header['Content-Length']
  47. return nil if len.nil?
  48. return len.to_i
  49. end
  50. end
  51. class Response
  52. attr_reader :header
  53. attr_accessor :body, :status, :status_message
  54. def initialize(status=200)
  55. @status = status
  56. @status_message = nil
  57. @header = Table.new
  58. end
  59. end
  60. ##
  61. # a case-insensitive Hash class for HTTP header
  62. #
  63. class Table
  64. include Enumerable
  65. def initialize(hash={})
  66. @hash = hash
  67. update(hash)
  68. end
  69. def [](key)
  70. @hash[key.to_s.capitalize]
  71. end
  72. def []=(key, value)
  73. @hash[key.to_s.capitalize] = value
  74. end
  75. def update(hash)
  76. hash.each {|k,v| self[k] = v}
  77. self
  78. end
  79. def each
  80. @hash.each {|k,v| yield k.capitalize, v }
  81. end
  82. def writeTo(port)
  83. each { |k,v| port << "#{k}: #{v}" << CRLF }
  84. end
  85. end # class Table
  86. # Helper Methods ------------------------------------------
  87. def http_header(header=nil)
  88. new_header = Table.new(DEFAULT_HEADER)
  89. new_header.update(header) unless header.nil?
  90. new_header["Connection"] = "close"
  91. new_header["Date"] = http_date(Time.now)
  92. new_header
  93. end
  94. def http_date( aTime )
  95. aTime.gmtime.strftime( "%a, %d %b %Y %H:%M:%S GMT" )
  96. end
  97. def http_resp(status_code, status_message=nil, header=nil, body=nil)
  98. status_message ||= StatusCodeMapping[status_code]
  99. str = ""
  100. str << "#{HTTP_PROTO} #{status_code} #{status_message}" << CRLF
  101. http_header(header).writeTo(str)
  102. str << CRLF
  103. str << body unless body.nil?
  104. str
  105. end
  106. # Main Serve Loop -----------------------------------------
  107. def serve(io)
  108. # perform IP authentification
  109. unless @handler.ip_auth_handler(io)
  110. io << http_resp(403, "Forbidden")
  111. return
  112. end
  113. # parse first line
  114. if io.gets =~ /^(\S+)\s+(\S+)\s+(\S+)/
  115. request = Request.new(io, $1, $2, $3)
  116. else
  117. io << http_resp(400, "Bad Request")
  118. return
  119. end
  120. # parse HTTP headers
  121. while (line=io.gets) !~ /^(\n|\r)/
  122. if line =~ /^([\w-]+):\s*(.*)$/
  123. request.header[$1] = $2.strip
  124. end
  125. end
  126. io.binmode
  127. response = Response.new
  128. # execute request handler
  129. @handler.request_handler(request, response)
  130. # write response back to the client
  131. io << http_resp(response.status, response.status_message,
  132. response.header, response.body)
  133. rescue Exception => e
  134. io << http_resp(500, "Internal Server Error")
  135. end
  136. end # class HttpServer