def request(method, uri = nil, body: nil, query: nil, headers: nil, opts: {})
      _retry_count ||= self.retry_limit

      if opts[:retry] == false
        _retry_count = 0
      elsif opts[:retry].to_i > 0
        _retry_count = opts[:retry]
      end

      headers ||= {}
      # if the first argument is a Parse::Request object, then construct it
      _request = nil
      if method.is_a?(Request)
        _request     = method
        method       = _request.method
        uri        ||= _request.path
        query      ||= _request.query
        body       ||= _request.body
        headers.merge! _request.headers
      else
        _request = Parse::Request.new(method, uri, body: body, headers: headers, opts: opts)
      end

      # http method
      method = method.downcase.to_sym
      # set the User-Agent
      headers[USER_AGENT_HEADER] = USER_AGENT_VERSION

      if opts[:cache] == false
        headers[Parse::Middleware::Caching::CACHE_CONTROL] = "no-cache"
      elsif opts[:cache].is_a?(Numeric)
        # specify the cache duration of this request
        headers[Parse::Middleware::Caching::CACHE_EXPIRES_DURATION] = opts[:cache].to_s
      end

      if opts[:use_master_key] == false
        headers[Parse::Middleware::Authentication::DISABLE_MASTER_KEY] = "true"
      end

      token = opts[:session_token]
      if token.present?
        token = token.session_token if token.respond_to?(:session_token)
        headers[Parse::Middleware::Authentication::DISABLE_MASTER_KEY] = "true"
        headers[Parse::Protocol::SESSION_TOKEN] = token
      end

      #if it is a :get request, then use query params, otherwise body.
      params = (method == :get ? query : body) || {}
      # if the path does not start with the '/1/' prefix, then add it to be nice.
      # actually send the request and return the body
      response_env = @conn.send(method, uri, params, headers)
      response = response_env.body
      response.request = _request

      case response.http_status
      when 401, 403
        warn "[Parse:AuthenticationError] #{response}"
        raise Parse::Error::AuthenticationError, response
      when 400, 408
        if response.code == Parse::Response::ERROR_TIMEOUT || response.code == 143 #"net/http: timeout awaiting response headers"
          warn "[Parse:TimeoutError] #{response}"
          raise Parse::Error::TimeoutError, response
        end
      when 404
        unless response.object_not_found?
          warn "[Parse:ConnectionError] #{response}"
          raise Parse::Error::ConnectionError, response
        end
      when 405, 406
        warn "[Parse:ProtocolError] #{response}"
        raise Parse::Error::ProtocolError, response
      when 429 # Request over the throttle limit
        warn "[Parse:RequestLimitExceededError] #{response}"
        raise Parse::Error::RequestLimitExceededError, response
      when 500, 503
        warn "[Parse:ServiceUnavailableError] #{response}"
        raise Parse::Error::ServiceUnavailableError, response
      end

      if response.error?
        if response.code <= Parse::Response::ERROR_SERVICE_UNAVAILABLE
          warn "[Parse:ServiceUnavailableError] #{response}"
          raise Parse::Error::ServiceUnavailableError, response
        elsif response.code <= 100
          warn "[Parse:ServerError] #{response}"
          raise Parse::Error::ServerError, response
        elsif response.code == Parse::Response::ERROR_EXCEEDED_BURST_LIMIT
          warn "[Parse:RequestLimitExceededError] #{response}"
          raise Parse::Error::RequestLimitExceededError, response
        elsif response.code == 209 # Error 209: invalid session token
          warn "[Parse:InvalidSessionTokenError] #{response}"
          raise Parse::Error::InvalidSessionTokenError, response
        end
      end

      response
    rescue Parse::Error::RequestLimitExceededError, Parse::Error::ServiceUnavailableError => e
      if _retry_count > 0
        warn "[Parse:Retry] Retries remaining #{_retry_count} : #{response.request}"
        _retry_count -= 1
        backoff_delay = RETRY_DELAY * (self.retry_limit - _retry_count)
        _retry_delay = [0,RETRY_DELAY, backoff_delay].sample
        sleep _retry_delay if _retry_delay > 0
        retry
      end
      raise
    rescue Faraday::Error::ClientError, Net::OpenTimeout => e
      if _retry_count > 0
        warn "[Parse:Retry] Retries remaining #{_retry_count} : #{_request}"
        _retry_count -= 1
        backoff_delay = RETRY_DELAY * (self.retry_limit - _retry_count)
        _retry_delay = [0,RETRY_DELAY, backoff_delay].sample
        sleep _retry_delay if _retry_delay > 0
        retry
      end
      raise Parse::Error::ConnectionError, "#{_request} : #{e.class} - #{e.message}"
    end
def request(method, uri = nil, body: nil, query: nil, headers: nil, opts: {})
      _retry_count ||= self.retry_limit

      if opts[:retry] == false
        _retry_count = 0
      elsif opts[:retry].to_i > 0
        _retry_count = opts[:retry]
      end

      headers ||= {}
      # if the first argument is a Parse::Request object, then construct it
      _request = nil
      if method.is_a?(Request)
        _request     = method
        method       = _request.method
        uri        ||= _request.path
        query      ||= _request.query
        body       ||= _request.body
        headers.merge! _request.headers
      else
        _request = Parse::Request.new(method, uri, body: body, headers: headers, opts: opts)
      end

      # http method
      method = method.downcase.to_sym
      # set the User-Agent
      headers[USER_AGENT_HEADER] = USER_AGENT_VERSION

      if opts[:cache] == false
        headers[Parse::Middleware::Caching::CACHE_CONTROL] = "no-cache"
      elsif opts[:cache].is_a?(Numeric)
        # specify the cache duration of this request
        headers[Parse::Middleware::Caching::CACHE_EXPIRES_DURATION] = opts[:cache].to_s
      end

      if opts[:use_master_key] == false
        headers[Parse::Middleware::Authentication::DISABLE_MASTER_KEY] = "true"
      end

      token = opts[:session_token]
      if token.present?
        token = token.session_token if token.respond_to?(:session_token)
        headers[Parse::Middleware::Authentication::DISABLE_MASTER_KEY] = "true"
        headers[Parse::Protocol::SESSION_TOKEN] = token
      end

      #if it is a :get request, then use query params, otherwise body.
      params = (method == :get ? query : body) || {}
      # if the path does not start with the '/1/' prefix, then add it to be nice.
      # actually send the request and return the body
      response_env = @conn.send(method, uri, params, headers)
      response = response_env.body
      response.request = _request

      case response.http_status
      when 401, 403
        warn "[Parse:AuthenticationError] #{response}"
        raise Parse::Error::AuthenticationError, response
      when 400, 408
        if response.code == Parse::Response::ERROR_TIMEOUT || response.code == 143 #"net/http: timeout awaiting response headers"
          warn "[Parse:TimeoutError] #{response}"
          raise Parse::Error::TimeoutError, response
        end
      when 404
        unless response.object_not_found?
          warn "[Parse:ConnectionError] #{response}"
          raise Parse::Error::ConnectionError, response
        end
      when 405, 406
        warn "[Parse:ProtocolError] #{response}"
        raise Parse::Error::ProtocolError, response
      when 429 # Request over the throttle limit
        warn "[Parse:RequestLimitExceededError] #{response}"
        raise Parse::Error::RequestLimitExceededError, response
      when 500, 503
        warn "[Parse:ServiceUnavailableError] #{response}"
        raise Parse::Error::ServiceUnavailableError, response
      end

      if response.error?
        if response.code <= Parse::Response::ERROR_SERVICE_UNAVAILABLE
          warn "[Parse:ServiceUnavailableError] #{response}"
          raise Parse::Error::ServiceUnavailableError, response
        elsif response.code <= 100
          warn "[Parse:ServerError] #{response}"
          raise Parse::Error::ServerError, response
        elsif response.code == Parse::Response::ERROR_EXCEEDED_BURST_LIMIT
          warn "[Parse:RequestLimitExceededError] #{response}"
          raise Parse::Error::RequestLimitExceededError, response
        elsif response.code == 209 # Error 209: invalid session token
          warn "[Parse:InvalidSessionTokenError] #{response}"
          raise Parse::Error::InvalidSessionTokenError, response
        end
      end

      response
    rescue Parse::Error::RequestLimitExceededError, Parse::Error::ServiceUnavailableError => e
      if _retry_count > 0
        warn "[Parse:Retry] Retries remaining #{_retry_count} : #{response.request}"
        _retry_count -= 1
        backoff_delay = RETRY_DELAY * (self.retry_limit - _retry_count)
        _retry_delay = [0,RETRY_DELAY, backoff_delay].sample
        sleep _retry_delay if _retry_delay > 0
        retry
      end
      raise
    rescue Faraday::Error::ClientError, Net::OpenTimeout => e
      if _retry_count > 0
        warn "[Parse:Retry] Retries remaining #{_retry_count} : #{_request}"
        _retry_count -= 1
        backoff_delay = RETRY_DELAY * (self.retry_limit - _retry_count)
        _retry_delay = [0,RETRY_DELAY, backoff_delay].sample
        sleep _retry_delay if _retry_delay > 0
        retry
      end
      raise Parse::Error::ConnectionError, "#{_request} : #{e.class} - #{e.message}"
    end
Send a REST API request to the server. This is the low-level API used for all requests
 to the Parse server with the provided options. Every request sent to Parse through
 the client goes through the configured set of middleware that can be modified by applying
 different headers or specific options.
 This method supports retrying requests a few times when a {Parse::ServiceUnavailableError}
 is raised.
 @param method [Symbol] The method type of the HTTP request (ex. :get, :post).
   - This parameter can also be a {Parse::Request} object.
 @param uri [String] the url path. It should not be an absolute url.
 @param body [Hash] the body of the request.
 @param query [Hash] the set of url query parameters to use in a GET request.
 @param headers [Hash] additional headers to apply to this request.
 @param opts [Hash] a set of options to pass through the middleware stack.
  - *:cache* [Integer] the number of seconds to cache this specific request.
    If set to `false`, caching will be disabled completely all together, which means even if
    a cached response exists, it will not be used.
  - *:use_master_key* [Boolean] whether this request should send the master key, if
    it was configured with {Parse.setup}. By default, if a master key was configured,
    all outgoing requests will contain it in the request header. Default `true`.
  - *:session_token* [String] The session token to send in this request. This disables
    sending the master key in the request, and sends this request with the credentials provided by
    the session_token.
  - *:retry* [Integer] The number of retrties to perform if the service is unavailable.
    Set to false to disable the retry mechanism. When performing request retries, the
    client will sleep for a number of seconds ({Parse::Client::RETRY_DELAY}) between requests.
    The default value is {Parse::Client::DEFAULT_RETRIES}.
 @raise Parse::Error::AuthenticationError when HTTP response status is 401 or 403
 @raise Parse::Error::TimeoutError when HTTP response status is 400 or
   408, and the Parse code is 143 or {Parse::Response::ERROR_TIMEOUT}.
 @raise Parse::Error::ConnectionError when HTTP response status is 404 is not an object not found error.
  - This will also be raised if after retrying a request a number of times has finally failed.
 @raise Parse::Error::ProtocolError when HTTP response status is 405 or 406
 @raise Parse::Error::ServiceUnavailableError when HTTP response status is 500 or 503.
   - This may also happen when the Parse Server response code is any
     number less than {Parse::Response::ERROR_SERVICE_UNAVAILABLE}.
 @raise Parse::Error::ServerError when the Parse response code is less than 100
 @raise Parse::Error::RequestLimitExceededError when the Parse response code is {Parse::Response::ERROR_EXCEEDED_BURST_LIMIT}.
   - This usually means you have exceeded the burst limit on requests, which will mean you will be throttled for the
     next 60 seconds.
 @raise Parse::Error::InvalidSessionTokenError when the Parse response code is 209.
   - This means the session token that was sent in the request seems to be invalid.
 @return [Parse::Response] the response for this request.
 @see Parse::Middleware::BodyBuilder
 @see Parse::Middleware::Caching
 @see Parse::Middleware::Authentication
 @see Parse::Protocol
 @see Parse::Request