ferencd@0: require 'net/http/server/parser' ferencd@0: require 'net/http/server/requests' ferencd@0: require 'net/http/server/responses' ferencd@0: require 'net/http/server/stream' ferencd@0: require 'net/http/server/chunked_stream' ferencd@0: ferencd@0: require 'net/protocol' ferencd@0: require 'gserver' ferencd@0: ferencd@0: module Net ferencd@0: class HTTP < Protocol ferencd@0: module Server ferencd@0: class Daemon < GServer ferencd@0: ferencd@0: include Requests ferencd@0: include Responses ferencd@0: ferencd@0: # Default host to bind to. ferencd@0: DEFAULT_HOST = '0.0.0.0' ferencd@0: ferencd@0: # Default port to listen on. ferencd@0: DEFAULT_PORT = 8080 ferencd@0: ferencd@0: # Maximum number of simultaneous connections. ferencd@0: MAX_CONNECTIONS = 256 ferencd@0: ferencd@0: # Creates a new HTTP Daemon. ferencd@0: # ferencd@0: # @param [Hash] options ferencd@0: # Options for the daemon. ferencd@0: # ferencd@0: # @option options [String] :host (DEFAULT_HOST) ferencd@0: # The host to run on. ferencd@0: # ferencd@0: # @option options [String] :port (DEFAULT_PORT) ferencd@0: # The port to listen on. ferencd@0: # ferencd@0: # @option options [Integer] :max_connections (MAX_CONNECTIONS) ferencd@0: # The maximum number of simultaneous connections. ferencd@0: # ferencd@0: # @option options [IO] :log ($stderr) ferencd@0: # The log to write errors to. ferencd@0: # ferencd@0: # @option options [#call] :handler ferencd@0: # The HTTP Request Handler object. ferencd@0: # ferencd@0: # @yield [request, socket] ferencd@0: # If a block is given, it will be used to process HTTP Requests. ferencd@0: # ferencd@0: # @yieldparam [Hash{Symbol => String,Array,Hash}] request ferencd@0: # The HTTP Request. ferencd@0: # ferencd@0: # @yieldparam [TCPSocket] socket ferencd@0: # The TCP socket of the client. ferencd@0: # ferencd@0: def initialize(options={},&block) ferencd@0: host = options.fetch(:host,DEFAULT_HOST) ferencd@0: port = options.fetch(:port,DEFAULT_PORT).to_i ferencd@0: max_connections = options.fetch(:max_connections,MAX_CONNECTIONS) ferencd@0: log = options.fetch(:log,$stderr) ferencd@0: ferencd@0: super(port,host,max_connections,log,false,true) ferencd@0: ferencd@0: handler(options[:handler],&block) ferencd@0: end ferencd@0: ferencd@0: # ferencd@0: # Sets the HTTP Request Handler. ferencd@0: # ferencd@0: # @param [#call, nil] object ferencd@0: # The HTTP Request Handler object. ferencd@0: # ferencd@0: # @yield [request, stream] ferencd@0: # If a block is given, it will be used to process HTTP Requests. ferencd@0: # ferencd@0: # @yieldparam [Hash{Symbol => String,Array,Hash}] request ferencd@0: # The HTTP Request. ferencd@0: # ferencd@0: # @yieldparam [Stream, ChunkedStream] stream ferencd@0: # The stream of the HTTP Request body. ferencd@0: # ferencd@0: # @raise [ArgumentError] ferencd@0: # The HTTP Request Handler must respond to `#call`. ferencd@0: # ferencd@0: def handler(object=nil,&block) ferencd@0: if object ferencd@0: unless object.respond_to?(:call) ferencd@0: raise(ArgumentError,"HTTP Request Handler must respond to #call") ferencd@0: end ferencd@0: elsif block.nil? ferencd@0: raise(ArgumentError,"no HTTP Request Handler block given") ferencd@0: end ferencd@0: ferencd@0: @handler = (object || block) ferencd@0: end ferencd@0: ferencd@0: # ferencd@0: # Receives HTTP Requests and handles them. ferencd@0: # ferencd@0: # @param [TCPSocket] socket ferencd@0: # A new TCP connection. ferencd@0: # ferencd@0: def serve(socket) ferencd@0: if (raw_request = read_request(socket)) ferencd@0: parser = Parser.new ferencd@0: ferencd@0: begin ferencd@0: request = parser.parse(raw_request) ferencd@0: rescue Parslet::ParseFailed ferencd@0: return Responses::BAD_REQUEST ferencd@0: end ferencd@0: ferencd@0: normalize_request(request) ferencd@0: ferencd@0: stream = if request[:headers]['Transfer-Encoding'] == 'chunked' ferencd@0: ChunkedStream.new(socket) ferencd@0: else ferencd@0: Stream.new(socket) ferencd@0: end ferencd@0: ferencd@0: # rack compliant ferencd@0: status, headers, body = @handler.call(request,stream) ferencd@0: ferencd@0: write_response(socket,status,headers,body) ferencd@0: end ferencd@0: end ferencd@0: ferencd@0: end ferencd@0: end ferencd@0: end ferencd@0: end