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