1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
5 defmodule Pleroma.Web.FedSockets do
7 This documents the FedSockets framework. A framework for federating
8 ActivityPub objects between servers via persistant WebSocket connections.
10 FedSockets allow servers to authenticate on first contact and maintain that
11 connection, eliminating the need to authenticate every time data needs to be shared.
14 FedSockets currently support 2 types of data transfer:
15 * `publish` method which doesn't require a response
16 * `fetch` method requires a response be sent
19 The publish operation sends a json encoded map of the shape:
20 %{action: :publish, data: json}
21 and accepts (but does not require) a reply of form:
22 %{"action" => "publish_reply"}
24 The outgoing params represent
25 * data: ActivityPub object encoded into json
29 The fetch operation sends a json encoded map of the shape:
30 %{action: :fetch, data: id, uuid: fetch_uuid}
31 and requires a reply of form:
32 %{"action" => "fetch_reply", "uuid" => uuid, "data" => data}
34 The outgoing params represent
35 * id: an ActivityPub object URI
36 * uuid: a unique uuid generated by the sender
38 The reply params represent
39 * data: an ActivityPub object encoded into json
40 * uuid: the uuid sent along with the fetch request
43 Clients of FedSocket transfers shouldn't need to use any of the functions outside of this module.
45 A typical publish operation can be performed through the following code, and a fetch operation in a similar manner.
47 case FedSockets.get_or_create_fed_socket(inbox) do
49 FedSockets.publish(fedsocket, json)
52 alternative_publish(inbox, actor, json, params)
56 FedSockets have the following config settings
58 config :pleroma, :fed_sockets,
60 ping_interval: :timer.seconds(15),
61 connection_duration: :timer.hours(1),
62 rejection_duration: :timer.hours(1),
68 * enabled - turn FedSockets on or off with this flag. Can be toggled at runtime.
69 * connection_duration - How long a FedSocket can sit idle before it's culled.
70 * rejection_duration - After failing to make a FedSocket connection a host will be excluded
71 from further connections for this amount of time
72 * fed_socket_fetches - Use these parameters to pass options to the Cachex queue backing the FetchRegistry
73 * fed_socket_rejections - Use these parameters to pass options to the Cachex queue backing the FedRegistry
76 * default: the minimum amount of time a fetch can wait before it times out.
77 * interval: the interval between checks for timed out entries. This plus the default represent the maximum time allowed
78 * lazy: leave at false for consistant and fast lookups, set to true for stricter timeout enforcement
83 alias Pleroma.Web.FedSockets.FedRegistry
84 alias Pleroma.Web.FedSockets.FedSocket
85 alias Pleroma.Web.FedSockets.SocketInfo
88 returns a FedSocket for the given origin. Will reuse an existing one or create a new one.
90 address is expected to be a fully formed URL such as:
91 "http://www.example.com" or "http://www.example.com:8080"
93 It can and usually does include additional path parameters,
94 but these are ignored as the FedSockets are organized by host and port info alone.
96 def get_or_create_fed_socket(address) do
97 with {:cache, {:error, :missing}} <- {:cache, get_fed_socket(address)},
98 {:connect, {:ok, _pid}} <- {:connect, FedSocket.connect_to_host(address)},
99 {:cache, {:ok, fed_socket}} <- {:cache, get_fed_socket(address)} do
100 Logger.debug("fedsocket created for - #{inspect(address)}")
103 {:cache, {:ok, socket}} ->
104 Logger.debug("fedsocket found in cache - #{inspect(address)}")
107 {:cache, {:error, :rejected} = e} ->
110 {:connect, {:error, _host}} ->
111 Logger.debug("set host rejected for - #{inspect(address)}")
112 FedRegistry.set_host_rejected(address)
115 {_, {:error, :disabled}} ->
118 {_, {:error, reason}} ->
119 Logger.warn("get_or_create_fed_socket error - #{inspect(reason)}")
125 returns a FedSocket for the given origin. Will not create a new FedSocket if one does not exist.
127 address is expected to be a fully formed URL such as:
128 "http://www.example.com" or "http://www.example.com:8080"
130 def get_fed_socket(address) do
131 origin = SocketInfo.origin(address)
133 with {:config, true} <- {:config, Pleroma.Config.get([:fed_sockets, :enabled], false)},
134 {:ok, socket} <- FedRegistry.get_fed_socket(origin) do
140 {:error, :rejected} ->
141 Logger.debug("FedSocket previously rejected - #{inspect(origin)}")
150 Sends the supplied data via the publish protocol.
151 It will not block waiting for a reply.
152 Returns :ok but this is not an indication of a successful transfer.
154 the data is expected to be JSON encoded binary data.
156 def publish(%SocketInfo{} = fed_socket, json) do
157 FedSocket.publish(fed_socket, json)
161 Sends the supplied data via the fetch protocol.
162 It will block waiting for a reply or timeout.
164 Returns {:ok, object} where object is the requested object (or nil)
165 {:error, :timeout} in the event the message was not responded to
167 the id is expected to be the URI of an ActivityPub object.
169 def fetch(%SocketInfo{} = fed_socket, id) do
170 FedSocket.fetch(fed_socket, id)
174 Disconnect all and restart FedSockets.
175 This is mainly used in development and testing but could be useful in production.
180 |> Process.exit(:testing)
183 def uri_for_origin(origin),
184 do: "ws://#{origin}/api/fedsocket/v1"