require "../server"

class HTTP::Server::RequestProcessor
  def initialize(&@handler : HTTP::Handler::HandlerProc)
    @wants_close = false
  end

  def initialize(@handler : HTTP::Handler | HTTP::Handler::HandlerProc)
    @wants_close = false
  end

  def close
    @wants_close = true
  end

  def process(input, output, error = STDERR)
    must_close = true
    response = Response.new(output)

    begin
      until @wants_close
        request = HTTP::Request.from_io(input)

        # EOF
        break unless request

        if request.is_a?(HTTP::Request::BadRequest)
          response.respond_with_error("Bad Request", 400)
          response.close
          return
        end

        response.version = request.version
        response.reset
        response.headers["Connection"] = "keep-alive" if request.keep_alive?
        context = Context.new(request, response)

        begin
          @handler.call(context)
        rescue ex
          response.respond_with_error
          response.close
          error.puts "Unhandled exception on HTTP::Handler"
          ex.inspect_with_backtrace(error)
          return
        end

        if response.upgraded?
          must_close = false
          return
        end

        response.output.close
        output.flush

        break unless request.keep_alive?

        # Don't continue if the handler set `Connection` header to `close`
        break unless HTTP.keep_alive?(response)

        # The request body is either FixedLengthContent or ChunkedContent.
        # In case it has not entirely been consumed by the handler, the connection is
        # closed the connection even if keep alive was requested.
        case body = request.body
        when FixedLengthContent
          if body.read_remaining > 0
            # Close the connection if there are bytes remaining
            break
          end
        when ChunkedContent
          # Close the connection if the IO has still bytes to read.
          break unless body.closed?
        end
      end
    rescue ex : Errno
      # IO-related error, nothing to do
    ensure
      begin
        input.close if must_close
      rescue ex : Errno
        # IO-related error, nothing to do
      end
    end
  end
end
