From ebc53c7f1bff9b3ee7a0a6efbea8ddf770408617 Mon Sep 17 00:00:00 2001 From: Justin Baker Date: Fri, 24 Nov 2017 11:34:56 -0600 Subject: [PATCH] Add `Conn.parse_url/1` - When a url without a path is parsed add a "/" path. - Fixes #38 - Allow parsing `http` and `https` schemes. - This is strictly for user experience, they act just like `ws` and `wss`. --- CHANGELOG.md | 3 +++ lib/websockex.ex | 21 ++------------------- lib/websockex/conn.ex | 25 ++++++++++++++++++++++++- test/websockex/conn_test.exs | 20 ++++++++++++++++++++ 4 files changed, 49 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cb87751..85af92c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,9 @@ - Allow `:via` and `:global` tuples for named registration. This includes handling for `cast/2` and `send_frame/2`. - Add access to response headers during `handle_connect/2` via `Conn.resp_headers`. +- Add `Conn.parse_url/1` to handle url to URI conversion. +- Automatically add a "/" path to a pathless url. + - The HTTP request will break without a valid path! ### Bug Fixes - No longer invoke `handle_disconnect` if there is reason to exit from invoking diff --git a/lib/websockex.ex b/lib/websockex.ex index b2c3f57..c23617a 100644 --- a/lib/websockex.ex +++ b/lib/websockex.ex @@ -282,7 +282,7 @@ defmodule WebSockex do Utils.spawn(:no_link, conn, module, state, opts) end def start(url, module, state, opts) do - case parse_uri(url) do + case WebSockex.Conn.parse_url(url) do {:ok, uri} -> conn = WebSockex.Conn.new(uri, opts) start(conn, module, state, opts) @@ -309,7 +309,7 @@ defmodule WebSockex do Utils.spawn(:link, conn, module, state, opts) end def start_link(url, module, state, opts) do - case parse_uri(url) do + case WebSockex.Conn.parse_url(url) do {:ok, uri} -> conn = WebSockex.Conn.new(uri, opts) start_link(conn, module, state, opts) @@ -942,23 +942,6 @@ defmodule WebSockex do end end - defp parse_uri(url) do - case URI.parse(url) do - %URI{port: port, scheme: protocol} when protocol in ["ws", "wss"] and is_nil(port) -> - # Someone may have deleted the URI config but I'm going to assume it's - # just that the application didn't get them registered. - {:error, %WebSockex.ApplicationError{reason: :not_started}} - # This is confusing to look at. But it's just a match with multiple guards - %URI{host: host, port: port, scheme: protocol} - when is_nil(host) - when is_nil(port) - when not protocol in ["ws", "wss"] -> - {:error, %WebSockex.URLError{url: url}} - %URI{} = uri -> - {:ok, uri} - end - end - defp purge_timer(ref, msg) do case Process.cancel_timer(ref) do i when is_integer(i) -> :ok diff --git a/lib/websockex/conn.ex b/lib/websockex/conn.ex index 3f90c76..37328be 100644 --- a/lib/websockex/conn.ex +++ b/lib/websockex/conn.ex @@ -57,7 +57,7 @@ defmodule WebSockex.Conn do @type t :: %__MODULE__{conn_mod: :gen_tcp | :ssl, host: String.t, port: non_neg_integer, - path: String.t | nil, + path: String.t, query: String.t | nil, extra_headers: [header], transport: transport, @@ -86,6 +86,29 @@ defmodule WebSockex.Conn do socket_recv_timeout: Keyword.get(opts, :socket_recv_timeout, @socket_recv_timeout_default)} end + @doc """ + Parses a url string for a valid URI + """ + @spec parse_url(String.t) :: {:ok, URI.t} | {:error, %WebSockex.URLError{}} + def parse_url(url) do + case URI.parse(url) do + %URI{port: port, scheme: protocol} when protocol in ["ws", "wss"] and is_nil(port) -> + # Someone may have deleted the URI config but I'm going to assume it's + # just that the application didn't get them registered. + {:error, %WebSockex.ApplicationError{reason: :not_started}} + # This is confusing to look at. But it's just a match with multiple guards + %URI{host: host, port: port, scheme: protocol} + when is_nil(host) + when is_nil(port) + when not protocol in ["ws", "wss", "http", "https"] -> + {:error, %WebSockex.URLError{url: url}} + %URI{path: nil} = uri -> + {:ok, %{uri | path: "/"}} + %URI{} = uri -> + {:ok, uri} + end + end + @doc """ Sends data using the `conn_mod` module. """ diff --git a/test/websockex/conn_test.exs b/test/websockex/conn_test.exs index c578bcf..1d3eb7d 100644 --- a/test/websockex/conn_test.exs +++ b/test/websockex/conn_test.exs @@ -72,6 +72,26 @@ defmodule WebSockex.ConnTest do socket_recv_timeout: 5000} end + test "parse_url" do + assert WebSockex.Conn.parse_url("lemon_pie") == + {:error, %WebSockex.URLError{url: "lemon_pie"}} + + ws_url = "ws://localhost/ws" + assert WebSockex.Conn.parse_url(ws_url) == {:ok, URI.parse(ws_url)} + + wss_url = "wss://localhost/ws" + assert WebSockex.Conn.parse_url(wss_url) == {:ok, URI.parse(wss_url)} + + http_url = "https://localhost/ws" + assert WebSockex.Conn.parse_url(http_url) == {:ok, URI.parse(http_url)} + + https_url = "https://localhost/wss" + assert WebSockex.Conn.parse_url(https_url) == {:ok, URI.parse(https_url)} + + pathless_url = "ws://localhost" + assert WebSockex.Conn.parse_url(pathless_url) == {:ok, %{URI.parse(pathless_url) | path: "/"}} + end + test "open_socket", context do %{host: host, port: port, path: path} = context.uri