Skip to content

Commit 4bf32ce

Browse files
Noarkhhmat-hek
andauthored
Change receiving (#44)
* Don't receive from the socket after transfer * Retry on 401 * Change handle_response signature * Remove no_response functions * Dont change socket mode after transfering control --------- Co-authored-by: Mateusz Front <mateusz.front@swmansion.com>
1 parent 4cd4834 commit 4bf32ce

File tree

6 files changed

+80
-81
lines changed

6 files changed

+80
-81
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ of dependencies in `mix.exs`:
1818
```elixir
1919
def deps do
2020
[
21-
{:membrane_rtsp, "~> 0.9.0"}
21+
{:membrane_rtsp, "~> 0.10.0"}
2222
]
2323
end
2424
```

lib/membrane_rtsp/rtsp.ex

Lines changed: 55 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ defmodule Membrane.RTSP do
66

77
require Logger
88
alias Membrane.RTSP
9-
alias Membrane.RTSP.{Request, Response, TCPSocket}
9+
alias Membrane.RTSP.{Request, Response, Transport}
1010

1111
@type t() :: pid()
1212

@@ -19,15 +19,6 @@ defmodule Membrane.RTSP do
1919

2020
defmodule State do
2121
@moduledoc false
22-
@enforce_keys [:socket, :uri]
23-
defstruct @enforce_keys ++
24-
[
25-
:response_timeout,
26-
:session_id,
27-
cseq: 0,
28-
auth: nil
29-
]
30-
3122
@type digest_opts() :: %{
3223
realm: String.t() | nil,
3324
nonce: String.t() | nil
@@ -41,8 +32,20 @@ defmodule Membrane.RTSP do
4132
uri: URI.t(),
4233
session_id: binary() | nil,
4334
auth: auth(),
44-
response_timeout: non_neg_integer()
35+
response_timeout: non_neg_integer() | nil,
36+
receive_from: :socket | :external_process,
37+
retries: non_neg_integer()
4538
}
39+
40+
@enforce_keys [:socket, :uri, :response_timeout]
41+
defstruct @enforce_keys ++
42+
[
43+
session_id: nil,
44+
cseq: 0,
45+
auth: nil,
46+
receive_from: :socket,
47+
retries: 0
48+
]
4649
end
4750

4851
@doc """
@@ -67,15 +70,14 @@ defmodule Membrane.RTSP do
6770
GenServer.call(session, {:execute, request}, :infinity)
6871
end
6972

70-
@spec request_no_response(pid(), binary(), RTSP.headers(), binary(), nil | binary()) :: :ok
71-
def request_no_response(session, method, headers \\ [], body \\ "", path \\ nil) do
72-
request = %Request{method: method, headers: headers, body: body, path: path}
73-
GenServer.cast(session, {:execute, request})
74-
end
75-
7673
@spec close(pid()) :: :ok
7774
def close(session), do: GenServer.cast(session, :terminate)
7875

76+
@doc """
77+
Transfer the control of the TCP socket the session was using to a new process. For more information see `:gen_tcp.controlling_process/2`.
78+
From now on the session won't try to receive responses to requests from the socket, since now an other process is controlling it.
79+
Instead of this, the session will synchronously wait for the response to be supplied with `handle_response/2`.
80+
"""
7981
@spec transfer_socket_control(t(), pid()) ::
8082
:ok | {:error, :closed | :not_owner | :badarg | :inet.posix()}
8183
def transfer_socket_control(session, new_controlling_process) do
@@ -87,19 +89,13 @@ defmodule Membrane.RTSP do
8789
GenServer.call(session, :get_socket)
8890
end
8991

90-
@type headers :: [{binary(), binary()}]
91-
92-
@spec handle_response(t(), binary()) :: Response.result()
93-
def handle_response(session, raw_response),
94-
do: GenServer.call(session, {:parse_response, raw_response})
95-
96-
@spec get_parameter_no_response(t(), headers(), binary()) :: :ok
97-
def get_parameter_no_response(session, headers \\ [], body \\ ""),
98-
do: request_no_response(session, "GET_PARAMETER", headers, body)
92+
@spec handle_response(t(), binary()) :: :ok
93+
def handle_response(session, raw_response) do
94+
send(session, {:raw_response, raw_response})
95+
:ok
96+
end
9997

100-
@spec play_no_response(t(), headers()) :: :ok
101-
def play_no_response(session, headers \\ []),
102-
do: request_no_response(session, "PLAY", headers, "")
98+
@type headers :: [{binary(), binary()}]
10399

104100
@spec describe(t(), headers()) :: Response.result()
105101
def describe(session, headers \\ []), do: request(session, "DESCRIBE", headers, "")
@@ -148,7 +144,7 @@ defmodule Membrane.RTSP do
148144
%URI{userinfo: info} when is_binary(info) -> :basic
149145
end
150146

151-
with {:ok, socket} <- TCPSocket.connect(url, options[:connection_timeout]) do
147+
with {:ok, socket} <- Transport.connect(url, options[:connection_timeout]) do
152148
state = %State{
153149
socket: socket,
154150
uri: url,
@@ -164,53 +160,28 @@ defmodule Membrane.RTSP do
164160

165161
@impl true
166162
def handle_call({:execute, request}, _from, state) do
167-
with {:ok, raw_response} <- execute(request, state),
168-
{:ok, response, state} <- parse_response(raw_response, state) do
169-
{:reply, {:ok, response}, state}
170-
else
171-
{:error, reason} -> {:reply, {:error, reason}, state}
172-
end
163+
handle_execute_call(request, true, state)
173164
end
174165

175166
@impl true
176-
def handle_call(
177-
{:transfer_socket_control, new_controlling_process},
178-
_from,
179-
%State{socket: socket} = state
180-
) do
181-
{:reply, :gen_tcp.controlling_process(socket, new_controlling_process), state}
167+
def handle_call({:transfer_socket_control, new_controlling_process}, _from, state) do
168+
{
169+
:reply,
170+
:gen_tcp.controlling_process(state.socket, new_controlling_process),
171+
%{state | receive_from: :external_process}
172+
}
182173
end
183174

184175
@impl true
185176
def handle_call(:get_socket, _from, %State{socket: socket} = state) do
186177
{:reply, socket, state}
187178
end
188179

189-
@impl true
190-
def handle_call({:parse_response, raw_response}, _from, state) do
191-
with {:ok, response, state} <- parse_response(raw_response, state) do
192-
{:reply, {:ok, response}, state}
193-
else
194-
{:error, reason} -> {:reply, {:error, reason}, state}
195-
end
196-
end
197-
198180
@impl true
199181
def handle_cast(:terminate, %State{} = state) do
200182
{:stop, :normal, state}
201183
end
202184

203-
@impl true
204-
def handle_cast({:execute, request}, %State{cseq: cseq} = state) do
205-
case execute(request, state, false) do
206-
:ok ->
207-
{:noreply, %State{state | cseq: cseq + 1}}
208-
209-
{:error, reason} ->
210-
raise "Error executing request #{inspect(request)}, reason: #{inspect(reason)}"
211-
end
212-
end
213-
214185
@impl true
215186
def handle_info({:tcp, _socket, data}, state) do
216187
Logger.warning("Received an unexpected packet, ignoring: #{inspect(data)}")
@@ -224,7 +195,7 @@ defmodule Membrane.RTSP do
224195

225196
@impl true
226197
def terminate(_reason, state) do
227-
TCPSocket.close(state.socket)
198+
Transport.close(state.socket)
228199
end
229200

230201
@spec do_start(binary() | URI.t(), options(), (module(), any() -> GenServer.on_start())) ::
@@ -242,9 +213,8 @@ defmodule Membrane.RTSP do
242213
end
243214
end
244215

245-
@spec execute(Request.t(), State.t(), boolean()) ::
246-
:ok | {:ok, binary()} | {:error, reason :: any()}
247-
defp execute(request, state, get_response \\ true) do
216+
@spec execute(Request.t(), State.t()) :: {:ok, binary()} | {:error, reason :: any()}
217+
defp execute(request, state) do
248218
%State{
249219
cseq: cseq,
250220
socket: socket,
@@ -260,10 +230,10 @@ defmodule Membrane.RTSP do
260230
|> Request.with_header("User-Agent", @user_agent)
261231
|> apply_credentials(uri, state.auth)
262232
|> Request.stringify(uri)
263-
|> TCPSocket.execute(socket, response_timeout, get_response)
233+
|> Transport.execute(socket, response_timeout, state.receive_from)
264234
end
265235

266-
@spec inject_session_header(Request.t(), binary()) :: Request.t()
236+
@spec inject_session_header(Request.t(), binary() | nil) :: Request.t()
267237
defp inject_session_header(request, session_id) do
268238
case session_id do
269239
nil -> request
@@ -303,6 +273,23 @@ defmodule Membrane.RTSP do
303273
end
304274
end
305275

276+
@spec handle_execute_call(Request.t(), boolean(), State.t()) ::
277+
{:reply, Response.result(), State.t()}
278+
defp handle_execute_call(request, retry, state) do
279+
with {:ok, raw_response} <- execute(request, state),
280+
{:ok, response, state} <- parse_response(raw_response, state) do
281+
case response do
282+
%Response{status: 401} when retry ->
283+
handle_execute_call(request, false, state)
284+
285+
response ->
286+
{:reply, {:ok, response}, state}
287+
end
288+
else
289+
{:error, reason} -> {:reply, {:error, reason}, state}
290+
end
291+
end
292+
306293
@spec do_apply_credentials(Request.t(), URI.t(), State.auth()) :: Request.t()
307294
defp do_apply_credentials(request, %URI{userinfo: info}, :basic) do
308295
encoded = Base.encode64(info)
Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1-
defmodule Membrane.RTSP.TCPSocket do
1+
defmodule Membrane.RTSP.Transport do
22
@moduledoc false
33
import Mockery.Macro
44

5+
alias Membrane.RTSP.Response
6+
57
@connection_timeout 1000
68
@response_timeout 5000
79

@@ -16,9 +18,9 @@ defmodule Membrane.RTSP.TCPSocket do
1618
)
1719
end
1820

19-
@spec execute(binary(), :gen_tcp.socket(), non_neg_integer() | nil, boolean()) ::
20-
:ok | {:ok, binary()} | {:error, :closed | :timeout | :inet.posix()}
21-
def execute(request, socket, response_timeout, true = _get_response) do
21+
@spec execute(binary(), :gen_tcp.socket(), non_neg_integer() | nil, :socket | :external_process) ::
22+
{:ok, binary()} | {:error, :closed | :timeout | :inet.posix()}
23+
def execute(request, socket, response_timeout, :socket) do
2224
:inet.setopts(socket, active: false)
2325

2426
result =
@@ -30,8 +32,14 @@ defmodule Membrane.RTSP.TCPSocket do
3032
result
3133
end
3234

33-
def execute(request, socket, _response_timeout, false = _get_response) do
34-
mockable(:gen_tcp).send(socket, request)
35+
def execute(request, socket, response_timeout, :external_process) do
36+
with :ok <- mockable(:gen_tcp).send(socket, request) do
37+
receive do
38+
{:raw_response, response} -> {:ok, response}
39+
after
40+
response_timeout || @response_timeout -> {:error, :timeout}
41+
end
42+
end
3543
end
3644

3745
@spec close(:gen_tcp.socket()) :: :ok
@@ -42,7 +50,7 @@ defmodule Membrane.RTSP.TCPSocket do
4250
defp recv(socket, response_timeout, length \\ 0, acc \\ <<>>) do
4351
case do_recv(socket, response_timeout, length, acc) do
4452
{:ok, data} ->
45-
case Membrane.RTSP.Response.verify_content_length(data) do
53+
case Response.verify_content_length(data) do
4654
{:ok, _expected, _received} ->
4755
{:ok, data}
4856

@@ -56,7 +64,11 @@ defmodule Membrane.RTSP.TCPSocket do
5664
end
5765

5866
defp do_recv(socket, response_timeout, length, acc) do
59-
case mockable(:gen_tcp).recv(socket, length, response_timeout || @response_timeout) do
67+
case mockable(:gen_tcp).recv(
68+
socket,
69+
length,
70+
response_timeout || @response_timeout
71+
) do
6072
{:ok, data} -> {:ok, acc <> data}
6173
{:error, reason} -> {:error, reason}
6274
end

mix.exs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
defmodule Membrane.RTSP.MixProject do
22
use Mix.Project
33

4-
@version "0.9.1"
4+
@version "0.10.0"
55
@github_url "https://github.com/membraneframework/membrane_rtsp"
66

77
def project do

mix.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
"elixir_uuid": {:hex, :elixir_uuid, "1.2.1", "dce506597acb7e6b0daeaff52ff6a9043f5919a4c3315abb4143f0b00378c097", [:mix], [], "hexpm", "f7eba2ea6c3555cea09706492716b0d87397b88946e6380898c2889d68585752"},
88
"erlex": {:hex, :erlex, "0.2.7", "810e8725f96ab74d17aac676e748627a07bc87eb950d2b83acd29dc047a30595", [:mix], [], "hexpm", "3ed95f79d1a844c3f6bf0cea61e0d5612a42ce56da9c03f01df538685365efb0"},
99
"ex_doc": {:hex, :ex_doc, "0.34.2", "13eedf3844ccdce25cfd837b99bea9ad92c4e511233199440488d217c92571e8", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "5ce5f16b41208a50106afed3de6a2ed34f4acfd65715b82a0b84b49d995f95c1"},
10-
"ex_sdp": {:hex, :ex_sdp, "0.17.0", "4c50e7814f01f149c0ccf258fba8428f8567dffecf1c416ec3f6aaaac607a161", [:mix], [{:bunch, "~> 1.3", [hex: :bunch, repo: "hexpm", optional: false]}, {:elixir_uuid, "~> 1.2", [hex: :elixir_uuid, repo: "hexpm", optional: false]}], "hexpm", "c7fe0625902be2a835b5fe6834a189f7db7639d2625c8e9d8b3564e6d704145f"},
10+
"ex_sdp": {:hex, :ex_sdp, "1.0.0", "c66cd66d60ad03ff1eecdc6db6a1b8a7b89fec260fcc22e8d6703fc5bbf430a3", [:mix], [{:bunch, "~> 1.3", [hex: :bunch, repo: "hexpm", optional: false]}, {:elixir_uuid, "~> 1.2", [hex: :elixir_uuid, repo: "hexpm", optional: false]}], "hexpm", "e165dff988b8ab9d93588636aa5f3f683e1f848fc63b78b12382c8fa3dd39216"},
1111
"file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"},
1212
"jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"},
1313
"makeup": {:hex, :makeup, "1.1.2", "9ba8837913bdf757787e71c1581c21f9d2455f4dd04cfca785c70bbfff1a76a3", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "cce1566b81fbcbd21eca8ffe808f33b221f9eee2cbc7a1706fc3da9ff18e6cac"},

test/membrane_rtsp/session/session_logic_test.exs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,14 @@ defmodule Membrane.RTSP.SessionLogicTest do
55

66
alias Membrane.RTSP
77
alias Membrane.RTSP.State
8-
alias Membrane.RTSP.{Request, TCPSocket}
8+
alias Membrane.RTSP.{Request, Transport}
99

1010
@response_header "RTSP/1.0 200 OK\r\n"
1111

1212
setup_all do
1313
uri = "rtsp://localhost:5554/vod/mp4:name.mov" |> URI.parse()
1414
mock(:gen_tcp, :connect, :gen_tcp.listen(0, []))
15-
{:ok, socket} = TCPSocket.connect(uri, 500)
15+
{:ok, socket} = Transport.connect(uri, 500)
1616

1717
state = %State{
1818
socket: socket,

0 commit comments

Comments
 (0)