Repository: whatyouhide/redix (elixir)

Issue:
To support, or to not support "rediss://" thats the question
Hey Ho,

thank you for your redis lib.

I am curious whats your opinion about supporting the rediss:// schema to enable ssl?
It is important to know that official redis cli does not support it. As redis does not support
TLS in its service. People are using stunnels to support this. Nevertheless e.g. AWS supports
TLS on redis.

See some context about TLS and redis here:
https://github.com/antirez/redis/issues/2178

+ Current iana assignment for the schema:
https://www.iana.org/assignments/uri-schemes/prov/rediss

Ruby lib supports it:
https://github.com/redis/redis-rb/blob/c061b7afd4578a10735be30bbaf45e9df7d616fe/README.md#ssltls-support

Golang as well:
https://github.com/go-redis/redis/blob/ce19d9c40266a99af7d77f4a2344e4dd905e5e55/options.go#L210

Would be interested to implement it for this lib as well.

Currently using this feature causes a schema detection error:
https://github.com/whatyouhide/redix/blob/master/lib/redix/uri.ex#L8

Greetings, Thomas

Pull request description:
Add support for connection strings with rediss scheme
Closes #123 

Patch:
diff --git a/docker-compose.yml b/docker-compose.yml
index 652a0a1..e807c74 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -41,3 +41,16 @@ services:
       - "26383:26383"
     sysctls:
       net.core.somaxconn: 1024
+
+  base_with_stunnel:
+    image: dweomer/stunnel
+    environment:
+      - STUNNEL_SERVICE=base
+      - STUNNEL_ACCEPT=6384
+      - STUNNEL_CONNECT=base:6379
+    links:
+      - base
+    ports:
+      - 6384:6384
+    sysctls:
+      net.core.somaxconn: 1024
diff --git a/lib/redix/uri.ex b/lib/redix/uri.ex
index 77c18a3..b656c9c 100644
--- a/lib/redix/uri.ex
+++ b/lib/redix/uri.ex
@@ -5,7 +5,7 @@ defmodule Redix.URI do
   def opts_from_uri(uri) when is_binary(uri) do
     %URI{host: host, port: port, scheme: scheme} = uri = URI.parse(uri)
 
-    unless scheme == "redis" do
+    unless scheme in ["redis", "rediss"] do
       raise ArgumentError, "expected scheme to be redis://, got: #{scheme}://"
     end
 
@@ -14,6 +14,7 @@ defmodule Redix.URI do
     |> put_if_not_nil(:port, port)
     |> put_if_not_nil(:password, password(uri))
     |> put_if_not_nil(:database, database(uri))
+    |> enable_ssl_if_secure_scheme(scheme)
   end
 
   defp password(%URI{userinfo: nil}) do
@@ -30,4 +31,9 @@ defmodule Redix.URI do
 
   defp put_if_not_nil(opts, _key, nil), do: opts
   defp put_if_not_nil(opts, key, value), do: Keyword.put(opts, key, value)
+
+  defp enable_ssl_if_secure_scheme(opts, "rediss"),
+    do: Keyword.put(opts, :ssl, true)
+
+  defp enable_ssl_if_secure_scheme(opts, _), do: opts
 end


Test patch:
diff --git a/test/redix/uri_test.exs b/test/redix/uri_test.exs
index 6585184..1b35aa5 100644
--- a/test/redix/uri_test.exs
+++ b/test/redix/uri_test.exs
@@ -47,4 +47,13 @@ defmodule Redix.URITest do
     assert opts[:host] == "localhost"
     assert is_nil(opts[:database])
   end
+
+  test "opts_from_uri/1: accepts rediss scheme" do
+    opts = opts_from_uri("rediss://example.com")
+    assert opts[:host] == "example.com"
+    assert opts[:ssl] == true
+    assert is_nil(opts[:port])
+    assert is_nil(opts[:database])
+    assert is_nil(opts[:password])
+  end
 end
diff --git a/test/redix_test.exs b/test/redix_test.exs
index d296404..c696011 100644
--- a/test/redix_test.exs
+++ b/test/redix_test.exs
@@ -72,6 +72,11 @@ defmodule RedixTest do
       assert Redix.command(pid, ["PING"]) == {:ok, "PONG"}
     end
 
+    test "using a rediss:// url" do
+      {:ok, pid} = Redix.start_link("rediss://localhost:6384/3")
+      assert Redix.command(pid, ["PING"]) == {:ok, "PONG"}
+    end
+
     test "name registration" do
       {:ok, pid} = Redix.start_link(name: :redix_server)
       assert Process.whereis(:redix_server) == pid