-
-
Notifications
You must be signed in to change notification settings - Fork 59
Support IPv6 nodes and transport options #374
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
e81a19e
f5e8052
3305615
6bfa807
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -102,9 +102,13 @@ defmodule Xandra.Cluster.ControlConnection do | |
|
|
||
| {transport_opts, connection_opts} = Keyword.pop(connection_opts, :transport_options, []) | ||
|
|
||
| # Construct the transport options. | ||
| {keyword_options, other_options} = | ||
| Enum.split_with(transport_opts, fn x -> Keyword.keyword?([x]) end) | ||
|
Comment on lines
+105
to
+107
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You don't need to do this. Do: options: @forced_transport_options ++ transport_optsas putting the forced options at the beginning overrides later options. Please double check this but I’m pretty sure 🙃
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I added a conflicting transport option here: I tried both flipped configurations to see how ordering affected option precedence, but in both cases the connection failed. |
||
|
|
||
| transport = %Transport{ | ||
| module: module, | ||
| options: Keyword.merge(transport_opts, @forced_transport_options) | ||
| options: Keyword.merge(keyword_options, @forced_transport_options) ++ other_options | ||
| } | ||
|
|
||
| {transport, connection_opts} | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -409,16 +409,21 @@ defmodule Xandra.Connection do | |
| nil -> data.original_options | ||
| end | ||
|
|
||
| # Construct the transport options. | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same here, but you can use transport_options = Keyword.get(options, :transport_options, [])
buffer = :proplists.get_value(transport_options, :buffer, @default_transport_buffer_size)
transport_options = [buffer: buffer] ++ @forced_transport_options ++ :proplists.delete(:buffer, transport_options)Should work but please double check 😬
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This ran into the same issue as in the other comment where I could override the
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Relevant to both of these comments, I've added tests to check if the forced transport opts are being respected.
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @erhlee-bird mh I’m confused: iex> {:ok, socket} = :gen_tcp.connect(~c"google.com", 80, [{:active, true}, {:active, false}])
{:ok, #Port<0.4>}
iex> :inet.getopts(socket, [:active])
{:ok, [active: false]}
Seems like overriding works here? |
||
| {keyword_options, other_options} = | ||
| options | ||
| |> Keyword.get(:transport_options, []) | ||
| |> Enum.split_with(fn x -> Keyword.keyword?([x]) end) | ||
|
|
||
| # Now, build the state from the options. | ||
| {address, port} = Keyword.fetch!(options, :node) | ||
|
|
||
| transport = %Transport{ | ||
| module: if(options[:encryption], do: :ssl, else: :gen_tcp), | ||
| options: | ||
| options | ||
| |> Keyword.get(:transport_options, []) | ||
| |> Keyword.put_new(:buffer, @default_transport_buffer_size) | ||
| |> Keyword.merge(@forced_transport_options) | ||
| (keyword_options | ||
| |> Keyword.put_new(:buffer, @default_transport_buffer_size) | ||
| |> Keyword.merge(@forced_transport_options)) ++ other_options | ||
| } | ||
|
|
||
| data = %__MODULE__{ | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -30,16 +30,35 @@ defmodule Xandra.OptionsValidators do | |
|
|
||
| @spec validate_node(term()) :: {:ok, {String.t(), integer()}} | {:error, String.t()} | ||
| def validate_node(value) when is_binary(value) do | ||
| case String.split(value, ":", parts: 2) do | ||
| [address, port] -> | ||
| case Integer.parse(port) do | ||
| {port, ""} when port in 0..65535 -> {:ok, {address, port}} | ||
| {port, ""} -> {:error, "invalid port (outside of the 0..65535 range): #{port}"} | ||
| _ -> {:error, "invalid node: #{inspect(value)}"} | ||
| end | ||
|
|
||
| [address] -> | ||
| {:ok, {address, 9042}} | ||
| # Remove surrounding square brackets from IPv6 values. | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Instead of this logic, we could "pop" out the port like this, regardless of the type of IP using case Regex.split(~r/:(?=\d+$)/, value) do
[address, port] ->
# Same as before
[address] ->
# Also same as before
end
This way we don't have to parse the IPv6 address, we can just leave what the user passed in
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I thought about doing that, but there's ambiguity if you have IPv6 addresses without a port. The first 2 examples below using the Regex method above parse the address incorrectly.
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Works with a lookbehind assertion: (need to then |
||
| value = value |> String.replace("[", "") |> String.replace("]", "") | ||
|
|
||
| {address, port} = | ||
| case String.split(value, ":") do | ||
| # FQDN or IPv4. | ||
| [address] -> | ||
| {address, "9042"} | ||
|
|
||
| # FQDN:PORT or IPv4:PORT. IPv6 addresses have to have at least 2 colons. | ||
| [address, port] -> | ||
| {address, port} | ||
|
|
||
| # IPv6. | ||
| splits -> | ||
| # Our IPv6 address can either parse as a valid address or have the | ||
| # additional port suffix. | ||
| with {:ok, {_, _, _, _, _, _, _, _}} <- | ||
| :inet.parse_address(value |> String.to_charlist()) do | ||
| {value, "9042"} | ||
| else | ||
| _ -> {splits |> Enum.drop(-1) |> Enum.join(":"), List.last(splits)} | ||
| end | ||
| end | ||
|
|
||
| case Integer.parse(port) do | ||
| {port, ""} when port in 0..65535 -> {:ok, {address, port}} | ||
| {port, ""} -> {:error, "invalid port (outside of the 0..65535 range): #{port}"} | ||
| _ -> {:error, "invalid node: #{inspect(value)}"} | ||
| end | ||
| end | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
{:list, :any}is a superset of:keyword_list. Maybe we can go with this instead:or just go with
{:list, :any}.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see that the
:gen_tcpoptions support additionally theinet_familyatoms as well as lists and binaries. I don't have any examples of what those might be, but, if you're okay with it, we could start with{:list, :any}.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep sure let's go with tat.