Commit 3d506444 authored by Alexander Mankuta's avatar Alexander Mankuta Committed by Tiago
Browse files

Add cookie deletion

The API follows Array API.

- `delete` allows deletion if individual cookies. These cookies have to
  be instances of Cookie class. The can be either previously manually
  created and added to the jar cookies, or cookies found in the jar.
- `reject!`/`delete_if` allows deletion with a block.
- `select!`/`filter!`/`keep_if` allows keeping only specific cookies,
  selected by the block, and removal of the rest.
- `clear` allows removal of all cookies, it also can remove cookies
  that would match a URI (similar to how `[]` selects cookies)
parent 7a1f6748
Loading
Loading
Loading
Loading
+33 −0
Original line number Diff line number Diff line
@@ -50,6 +50,39 @@ module HTTPX
        @cookies << c
      end

      def delete(cookie)
        @cookies.delete(cookie)
      end

      def reject!(&block)
        return to_enum(__method__) unless block_given?

        @cookies.reject!(&block)
        self
      end
      alias_method :delete_if, :reject!

      def select!(&block)
        return to_enum(__method__) unless block_given?

        @cookies.select!(&block)
        self
      end
      alias_method :keep_if, :select!
      alias_method :filter!, :select!

      def clear(uri = nil)
        unless uri
          @cookies.clear
          return
        end

        tpath = uri.path
        @cookies.delete_if do |cookie|
          cookie.valid_for_uri?(uri) && Cookie.path_match?(cookie.path, tpath)
        end
      end

      def [](uri)
        each(uri).sort
      end
+13 −0
Original line number Diff line number Diff line
@@ -11,6 +11,19 @@ module HTTPX

      def add: (Cookie name, ?String path) -> void

      def delete: (Cookie cookie) -> Cookie?

      def reject!: () { (Cookie cookie) -> boolish } -> self?
                 | () -> ::Enumerator[Cookie, self?]
      alias delete_if reject!

      def select!: () { (Cookie cookie) -> boolish } -> self?
                 | () -> ::Enumerator[Cookie, self?]
      alias keep_if select!
      alias filter! select!

      def clear: (?http_uri?) -> void

      def []: (http_uri) -> Array[Cookie]

      def each: (?http_uri?) { (Cookie) -> void } -> void
+101 −0
Original line number Diff line number Diff line
@@ -107,6 +107,107 @@ class CookieJarTest < Minitest::Test
    assert [c4, c3, c2, c1].sort == [c3, c4, c1, c2]
  end

  def test_plugin_cookies_jar_clear
    HTTPX.plugin(:cookies) # force loading the modules

    # Clear all cookies
    jar = HTTPX::Plugins::Cookies::Jar.new
    jar.parse(%(a=b))
    jar.parse(%(a=b; Path=/; Domain=.google.com))
    jar.clear
    assert jar.each.to_a.empty?

    # Clear domain cookies
    jar = HTTPX::Plugins::Cookies::Jar.new
    jar.parse(%(a=b; Path=/; Domain=.example.com, a=b; Path=/; Domain=.google.com))
    jar.clear(URI("https://google.com/"))
    assert jar[URI("https://google.com/")].each.to_a.empty?
    assert !jar[URI("https://example.com/")].each.to_a.empty?
  end

  def test_plugin_cookies_jar_delete
    HTTPX.plugin(:cookies) # force loading the modules

    # Delete a cookie
    jar = HTTPX::Plugins::Cookies::Jar.new
    cookie = HTTPX::Plugins::Cookies::Cookie.new("a", "b")
    jar.add(cookie)
    jar.delete(cookie)
    assert jar.each.to_a.empty?

    # Deleting cookies not in the jar is a noop
    jar = HTTPX::Plugins::Cookies::Jar.new
    cookie = HTTPX::Plugins::Cookies::Cookie.new("a", "b")
    cookie2 = HTTPX::Plugins::Cookies::Cookie.new("x", "y")
    jar.add(cookie)
    jar.delete(cookie2)
    assert jar.each.to_a == [cookie]
  end

  def test_plugin_cookies_jar_reject
    HTTPX.plugin(:cookies) # force loading the modules

    # Delete a cookies with a block
    jar = HTTPX::Plugins::Cookies::Jar.new
    jar.parse(%(a=b; Path=/; Domain=.example.com, a=b; Path=/; Domain=.google.com))
    jar.reject! { |cookie| cookie.domain == "google.com" }
    assert jar[URI("https://google.com/")].each.to_a.empty?
    assert !jar[URI("https://example.com/")].each.to_a.empty?

    # Cookies can be rejected by any criteria
    jar = HTTPX::Plugins::Cookies::Jar.new
    jar.parse(%(a=1, b=2, c=1, d=2))
    jar.reject! { |cookie| cookie.value == "2" }
    assert jar.each.to_a.map(&:name) == %w[a c]

    # The method returns an enumerator if block is missing
    jar = HTTPX::Plugins::Cookies::Jar.new
    jar.parse(%(a=1, b=2, c=1, d=2))
    jar.reject!.with_index { |_cookie, index| index == 2 }
    assert jar.each.to_a.map(&:name) == %w[a b d]

    # delete_if does the same thing
    jar = HTTPX::Plugins::Cookies::Jar.new
    jar.parse(%(a=1, b=2, c=1, d=2))
    jar.delete_if { |cookie| cookie.value == "2" }
    assert jar.each.to_a.map(&:name) == %w[a c]
  end

  def test_plugin_cookies_jar_select
    HTTPX.plugin(:cookies) # force loading the modules

    # Keep a cookies with a block
    jar = HTTPX::Plugins::Cookies::Jar.new
    jar.parse(%(a=b; Path=/; Domain=.example.com, a=b; Path=/; Domain=.google.com))
    jar.select! { |cookie| cookie.domain == "google.com" }
    assert !jar[URI("https://google.com/")].each.to_a.empty?
    assert jar[URI("https://example.com/")].each.to_a.empty?

    # Cookies can be selected by any criteria
    jar = HTTPX::Plugins::Cookies::Jar.new
    jar.parse(%(a=1, b=2, c=1, d=2))
    jar.select! { |cookie| cookie.value == "1" }
    assert jar.each.to_a.map(&:name) == %w[a c]

    # The method returns an enumerator if block is missing
    jar = HTTPX::Plugins::Cookies::Jar.new
    jar.parse(%(a=1, b=2, c=1, d=2))
    jar.select!.with_index { |_cookie, index| index != 2 }
    assert jar.each.to_a.map(&:name) == %w[a b d]

    # keep_if does the same thing
    jar = HTTPX::Plugins::Cookies::Jar.new
    jar.parse(%(a=1, b=2, c=1, d=2))
    jar.keep_if { |cookie| cookie.value == "1" }
    assert jar.each.to_a.map(&:name) == %w[a c]

    # filter! does the same thing
    jar = HTTPX::Plugins::Cookies::Jar.new
    jar.parse(%(a=1, b=2, c=1, d=2))
    jar.filter! { |cookie| cookie.value == "1" }
    assert jar.each.to_a.map(&:name) == %w[a c]
  end

  private

  def jar_cookies_uri(path = "/cookies", scheme: "http")