distributed-press-api-client 0.3.0 → 0.4.0rc3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/distributed_press/v1/schemas/bittorrent_protocol.rb +23 -0
- data/lib/distributed_press/v1/schemas/new_site.rb +1 -0
- data/lib/distributed_press/v1/schemas/site.rb +4 -1
- data/lib/distributed_press/v1/schemas/update_site.rb +1 -1
- data/lib/distributed_press/v1/schemas.rb +1 -0
- data/lib/distributed_press/v1/social/allowlist.rb +18 -0
- data/lib/distributed_press/v1/social/blocklist.rb +75 -0
- data/lib/distributed_press/v1/social/client.rb +220 -86
- data/lib/distributed_press/v1/social/dereferencer.rb +89 -0
- data/lib/distributed_press/v1/social/dereferencer.rb.orig +132 -0
- data/lib/distributed_press/v1/social/hook.rb +100 -0
- data/lib/distributed_press/v1/social/inbox.rb +66 -0
- data/lib/distributed_press/v1/social/outbox.rb +50 -0
- data/lib/distributed_press/v1/social/reference.rb +39 -0
- data/lib/distributed_press/v1/social/referenced_object.rb +89 -0
- data/lib/distributed_press/v1/social/schemas/webhook.rb +24 -0
- data/lib/distributed_press/v1/social/signed_headers.rb +106 -0
- data/lib/distributed_press/version.rb +1 -1
- data/lib/dry/schema/processor_decorator.rb +25 -0
- data/lib/dry/schema/result_decorator.rb +35 -0
- metadata +46 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ee95dc9deb3cb19084a66ee33ee4970906619c6489ce0bf29b0aa9f90ae99d93
|
4
|
+
data.tar.gz: abcb6a90f1e16ad51aac21f15b79067c141f4cdc45edfbddc69704dd235adac7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5d3e98400b29ee2258a29beb1c47fab70a07d1cbdd43a96df9842a507d2ee3f3aaf9d3eeef1dee381967363227fb8af68b0b9f080861feac67595b1a081c43b0
|
7
|
+
data.tar.gz: 55291cec6e026520a3e9f111fd80e55b4762aa551f1dcae12015951a01d9d6dd55bbda07394f5f36566e3500abed7673e2a71c90feaf2f4092ef838361d3c8e8
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'dry-schema'
|
4
|
+
|
5
|
+
class DistributedPress
|
6
|
+
module V1
|
7
|
+
module Schemas
|
8
|
+
# Bittorrent protocol
|
9
|
+
class BittorrentProtocol < Dry::Schema::JSON
|
10
|
+
define do
|
11
|
+
required(:enabled).value(:bool)
|
12
|
+
# TODO: Validate URL
|
13
|
+
required(:link).filled(:string)
|
14
|
+
required(:gateway).filled(:string)
|
15
|
+
required(:dnslink).filled(:string)
|
16
|
+
required(:infoHash).filled(:string)
|
17
|
+
required(:pubKey).filled(:string)
|
18
|
+
required(:magnet).filled(:string)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -4,6 +4,7 @@ require 'dry-schema'
|
|
4
4
|
require_relative 'http_protocol'
|
5
5
|
require_relative 'hyper_protocol'
|
6
6
|
require_relative 'ipfs_protocol'
|
7
|
+
require_relative 'bittorrent_protocol'
|
7
8
|
|
8
9
|
class DistributedPress
|
9
10
|
module V1
|
@@ -15,18 +16,20 @@ class DistributedPress
|
|
15
16
|
|
16
17
|
# TODO: Validate domain name
|
17
18
|
required(:domain).filled(:string)
|
18
|
-
|
19
|
+
optional(:public).filled(:bool)
|
19
20
|
|
20
21
|
required(:protocols).hash do
|
21
22
|
required(:http).filled(:bool)
|
22
23
|
required(:ipfs).filled(:bool)
|
23
24
|
required(:hyper).filled(:bool)
|
25
|
+
optional(:bittorrent).filled(:bool)
|
24
26
|
end
|
25
27
|
|
26
28
|
required(:links).hash do
|
27
29
|
optional(:http).hash(HttpProtocol.new)
|
28
30
|
optional(:hyper).hash(HyperProtocol.new)
|
29
31
|
optional(:ipfs).hash(IpfsProtocol.new)
|
32
|
+
optional(:bittorrent).hash(BittorrentProtocol.new)
|
30
33
|
end
|
31
34
|
end
|
32
35
|
end
|
@@ -4,6 +4,7 @@ require_relative 'schemas/admin'
|
|
4
4
|
require_relative 'schemas/http_protocol'
|
5
5
|
require_relative 'schemas/hyper_protocol'
|
6
6
|
require_relative 'schemas/ipfs_protocol'
|
7
|
+
require_relative 'schemas/bittorrent_protocol'
|
7
8
|
require_relative 'schemas/new_admin'
|
8
9
|
require_relative 'schemas/new_publisher'
|
9
10
|
require_relative 'schemas/new_site'
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'blocklist'
|
4
|
+
|
5
|
+
class DistributedPress
|
6
|
+
module V1
|
7
|
+
module Social
|
8
|
+
# Manages Social Inbox allowlist
|
9
|
+
#
|
10
|
+
# @example
|
11
|
+
# DistributedPress::V1::Social::Allowlist.new(client: client)
|
12
|
+
# DistributedPress::V1::Social::Allowlist.new(client: client, actor: '@sutty@sutty.nl')
|
13
|
+
class Allowlist < Blocklist
|
14
|
+
ENDPOINT = '/allowlist'
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'client'
|
4
|
+
|
5
|
+
class DistributedPress
|
6
|
+
module V1
|
7
|
+
module Social
|
8
|
+
# Manages Social Inbox blocklist
|
9
|
+
#
|
10
|
+
# @example
|
11
|
+
# DistributedPress::V1::Social::Blocklist.new(client: client)
|
12
|
+
# DistributedPress::V1::Social::Blocklist.new(client: client, actor: '@sutty@sutty.nl')
|
13
|
+
class Blocklist
|
14
|
+
ACCEPT = %w[text/plain].freeze
|
15
|
+
CONTENT_TYPE = 'text/plain'
|
16
|
+
ENDPOINT = '/blocklist'
|
17
|
+
|
18
|
+
# @return [DistributedPress::V1::Social::Client]
|
19
|
+
attr_reader :client
|
20
|
+
|
21
|
+
# @return [String,nil]
|
22
|
+
attr_reader :actor
|
23
|
+
|
24
|
+
# @param :client [DistributedPress::V1::Social::Client]
|
25
|
+
# @param :actor [String]
|
26
|
+
def initialize(client:, actor: nil)
|
27
|
+
@client = client
|
28
|
+
@actor = actor
|
29
|
+
|
30
|
+
@serializer = proc do |body|
|
31
|
+
body.join("\n")
|
32
|
+
end
|
33
|
+
|
34
|
+
# @param :body [String,nil]
|
35
|
+
# @return [Array<String>]
|
36
|
+
@parser =
|
37
|
+
proc do |body, _|
|
38
|
+
next [] if body.nil?
|
39
|
+
|
40
|
+
body.split("\n").map(&:strip).reject(&:empty?)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# Obtains the current blocklist
|
45
|
+
#
|
46
|
+
# @return [Array<String>]
|
47
|
+
def get
|
48
|
+
client.get(endpoint: endpoint, parser: @parser, content_type: CONTENT_TYPE, accept: ACCEPT)
|
49
|
+
end
|
50
|
+
|
51
|
+
# Sends an array of instances and accounts to be blocked
|
52
|
+
#
|
53
|
+
# @param :list [Array<String>]
|
54
|
+
# @return [HTTParty::Response]
|
55
|
+
def post(list:)
|
56
|
+
client.post(endpoint: endpoint, body: list, serializer: @serializer, parser: @parser,
|
57
|
+
content_type: CONTENT_TYPE, accept: ACCEPT)
|
58
|
+
end
|
59
|
+
|
60
|
+
# Removes instances and accounts from the blocklist
|
61
|
+
#
|
62
|
+
# @param :list [Array<String>]
|
63
|
+
# @return [HTTParty::Response]
|
64
|
+
def delete(list:)
|
65
|
+
client.delete(endpoint: endpoint, body: list, serializer: @serializer, parser: @parser,
|
66
|
+
content_type: CONTENT_TYPE, accept: ACCEPT)
|
67
|
+
end
|
68
|
+
|
69
|
+
def endpoint
|
70
|
+
@endpoint ||= "/v1/#{actor}#{self.class::ENDPOINT}".squeeze('/')
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -1,10 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'httparty'
|
4
|
+
require 'httparty/cache'
|
4
5
|
require 'openssl'
|
5
6
|
require 'time'
|
6
7
|
require 'digest/sha2'
|
7
8
|
require 'uri'
|
9
|
+
require_relative '../../version'
|
10
|
+
require_relative 'signed_headers'
|
8
11
|
|
9
12
|
class DistributedPress
|
10
13
|
module V1
|
@@ -17,22 +20,33 @@ class DistributedPress
|
|
17
20
|
# @see {https://github.com/mastodon/mastodon/blob/main/app/lib/request.rb}
|
18
21
|
class Client
|
19
22
|
include ::HTTParty
|
23
|
+
include ::HTTParty::Cache
|
20
24
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
#
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
25
|
+
class Error < StandardError; end
|
26
|
+
class ContentTooLargeError < Error; end
|
27
|
+
class BrotliUnsupportedError < Error; end
|
28
|
+
|
29
|
+
# Always cache
|
30
|
+
caching true
|
31
|
+
|
32
|
+
# Content types sent and accepted
|
33
|
+
ACCEPT = %w[application/activity+json application/ld+json application/json].freeze
|
34
|
+
CONTENT_TYPE = 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"'
|
35
|
+
|
36
|
+
ACCEPT.each do |accept|
|
37
|
+
HTTParty::Parser::SupportedFormats[accept] = :json
|
38
|
+
end
|
31
39
|
|
32
40
|
# API URL
|
41
|
+
#
|
33
42
|
# @return [String]
|
34
43
|
attr_reader :url
|
35
44
|
|
45
|
+
# API URI
|
46
|
+
#
|
47
|
+
# @return [URI]
|
48
|
+
attr_reader :uri
|
49
|
+
|
36
50
|
# RSA key size
|
37
51
|
#
|
38
52
|
# @return [Integer]
|
@@ -43,36 +57,57 @@ class DistributedPress
|
|
43
57
|
# @return [String]
|
44
58
|
attr_reader :public_key_url
|
45
59
|
|
60
|
+
# Logger
|
61
|
+
#
|
62
|
+
# @return [Logger]
|
63
|
+
attr_reader :logger
|
64
|
+
|
46
65
|
# @param :url [String] Social Distributed Press URL
|
47
66
|
# @param :public_key [String] URL where public key is available
|
48
67
|
# @param :private_key_pem [String] RSA Private key, PEM encoded
|
49
68
|
# @param :key_size [Integer]
|
50
69
|
# @param :logger [Logger]
|
51
|
-
|
52
|
-
|
53
|
-
|
70
|
+
# @param :cache_store [HTTParty::Cache::Store::Abstract,Symbol]
|
71
|
+
def initialize(public_key_url:, url: 'https://social.distributed.press', private_key_pem: nil, key_size: 2048,
|
72
|
+
logger: nil, cache_store: :memory)
|
73
|
+
self.class.default_options[:logger] = @logger = logger
|
54
74
|
self.class.default_options[:log_level] = :debug
|
75
|
+
self.class.cache_store cache_store
|
55
76
|
|
77
|
+
@url = HTTParty.normalize_base_uri(url)
|
56
78
|
@public_key_url = public_key_url
|
57
79
|
@key_size = key_size
|
58
80
|
@private_key_pem = private_key_pem
|
59
|
-
|
60
|
-
|
61
|
-
|
81
|
+
@uri = URI.parse(url)
|
82
|
+
@serializer ||= proc do |body|
|
83
|
+
body.to_json
|
62
84
|
end
|
63
85
|
end
|
64
86
|
|
87
|
+
def _dump(_)
|
88
|
+
Marshal.dump({
|
89
|
+
public_key_url: public_key_url,
|
90
|
+
url: url,
|
91
|
+
private_key_pem: @private_key_pem,
|
92
|
+
key_size: key_size,
|
93
|
+
cache_store: self.class.cache_store
|
94
|
+
})
|
95
|
+
end
|
96
|
+
|
97
|
+
def self._load(hash)
|
98
|
+
new(**Marshal.load(hash))
|
99
|
+
end
|
100
|
+
|
65
101
|
# GET request
|
66
102
|
#
|
67
103
|
# @param endpoint [String]
|
68
|
-
# @
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
self.class.get(endpoint, headers: headers)
|
104
|
+
# @param parser [HTTParty::Parser,Proc]
|
105
|
+
# @param content_type [String]
|
106
|
+
# @param accept [Array<String>]
|
107
|
+
# @return [HTTParty::Response]
|
108
|
+
def get(endpoint:, parser: HTTParty::Parser, content_type: CONTENT_TYPE, accept: ACCEPT)
|
109
|
+
perform_signed_request method: Net::HTTP::Get, endpoint: endpoint, parser: parser, content_type: content_type,
|
110
|
+
accept: accept
|
76
111
|
end
|
77
112
|
|
78
113
|
# POST request
|
@@ -80,16 +115,45 @@ class DistributedPress
|
|
80
115
|
# @todo Use DRY-schemas
|
81
116
|
# @param endpoint [String]
|
82
117
|
# @param body [Hash]
|
83
|
-
# @
|
84
|
-
|
85
|
-
|
86
|
-
|
118
|
+
# @param serializer [Proc]
|
119
|
+
# @param parser [HTTParty::Parser,Proc]
|
120
|
+
# @param content_type [String]
|
121
|
+
# @param accept [Array<String>]
|
122
|
+
# @return [HTTParty::Response]
|
123
|
+
def post(endpoint:, body: nil, serializer: @serializer, parser: HTTParty::Parser, content_type: CONTENT_TYPE,
|
124
|
+
accept: ACCEPT)
|
125
|
+
perform_signed_request method: Net::HTTP::Post, endpoint: endpoint, body: body, serializer: serializer,
|
126
|
+
parser: parser, content_type: content_type, accept: accept
|
127
|
+
end
|
87
128
|
|
88
|
-
|
89
|
-
|
90
|
-
|
129
|
+
# PUT request
|
130
|
+
#
|
131
|
+
# @param endpoint [String]
|
132
|
+
# @param body [Hash]
|
133
|
+
# @param serializer [Proc]
|
134
|
+
# @param parser [HTTParty::Parser,Proc]
|
135
|
+
# @param content_type [String]
|
136
|
+
# @param accept [Array<String>]
|
137
|
+
# @return [HTTParty::Response]
|
138
|
+
def put(endpoint:, body: nil, serializer: @serializer, parser: HTTParty::Parser, content_type: CONTENT_TYPE,
|
139
|
+
accept: ACCEPT)
|
140
|
+
perform_signed_request method: Net::HTTP::Put, endpoint: endpoint, body: body, serializer: serializer,
|
141
|
+
parser: parser, content_type: content_type, accept: accept
|
142
|
+
end
|
91
143
|
|
92
|
-
|
144
|
+
# DELETE request with optional body
|
145
|
+
#
|
146
|
+
# @param endpoint [String]
|
147
|
+
# @param body [Hash]
|
148
|
+
# @param serializer [Proc]
|
149
|
+
# @param parser [HTTParty::Parser,Proc]
|
150
|
+
# @param content_type [String]
|
151
|
+
# @param accept [Array<String>]
|
152
|
+
# @return [HTTParty::Response]
|
153
|
+
def delete(endpoint:, body: nil, serializer: @serializer, parser: HTTParty::Parser, content_type: CONTENT_TYPE,
|
154
|
+
accept: ACCEPT)
|
155
|
+
perform_signed_request method: Net::HTTP::Delete, endpoint: endpoint, body: body, serializer: serializer,
|
156
|
+
parser: parser, content_type: content_type, accept: accept
|
93
157
|
end
|
94
158
|
|
95
159
|
# Loads or generates a private key
|
@@ -115,84 +179,154 @@ class DistributedPress
|
|
115
179
|
|
116
180
|
private
|
117
181
|
|
118
|
-
|
119
|
-
|
182
|
+
# Perform request
|
183
|
+
#
|
184
|
+
# @param method [Net::HTTP::Get,Net::HTTP::Post,Net::HTTP::Put,Net::HTTP::Delete]
|
185
|
+
# @param endpoint [String]
|
186
|
+
# @param body [Hash]
|
187
|
+
# @param serializer [Proc]
|
188
|
+
# @param parser [HTTParty::Parser,Proc]
|
189
|
+
# @param content_type [String]
|
190
|
+
# @param accept [Array<String>]
|
191
|
+
# @return [HTTParty::Response]
|
192
|
+
def perform_signed_request(method:, endpoint:, body: nil, serializer: @default_serializer, parser: HTTParty::Parser,
|
193
|
+
content_type: CONTENT_TYPE, accept: ACCEPT)
|
194
|
+
headers = default_headers(content_type: content_type, accept: accept)
|
195
|
+
|
196
|
+
unless body.nil?
|
197
|
+
body = serializer.call(body)
|
198
|
+
checksum_body!(body, headers)
|
199
|
+
end
|
200
|
+
|
201
|
+
# Mimics HTTParty.perform_request, but processing the header
|
202
|
+
# after the request is instantiated. No need to process
|
203
|
+
# cookies for now. Uses the public key URL as a caching key so
|
204
|
+
# we revalidate access on shared caches.
|
205
|
+
options = { body: body, headers: headers, base_uri: url, parser: parser, stream_body: true, cache_key: public_key_url }
|
206
|
+
options = HTTParty::ModuleInheritableAttributes.hash_deep_dup(self.class.default_options).merge(options)
|
207
|
+
response_body = ''.dup
|
208
|
+
|
209
|
+
HTTParty::Request.new(method, endpoint, options).tap do |request|
|
210
|
+
headers.request = request
|
211
|
+
end.perform do |fragment|
|
212
|
+
fragment_to_body!(fragment, response_body)
|
213
|
+
end.tap do |response|
|
214
|
+
next if response_body.empty?
|
215
|
+
|
216
|
+
fix_response!(response, response_body)
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
# Default headers
|
221
|
+
#
|
222
|
+
# @param :content_type [String]
|
223
|
+
# @param :accept [Array<String>]
|
224
|
+
# @return [SignedHeaders]
|
225
|
+
def default_headers(content_type: CONTENT_TYPE, accept: ACCEPT)
|
226
|
+
SignedHeaders[
|
120
227
|
'User-Agent' => "DistributedPress/#{DistributedPress::VERSION}",
|
121
|
-
'Content-Type' =>
|
122
|
-
'
|
123
|
-
|
228
|
+
'Content-Type' => content_type,
|
229
|
+
'Accept' => accept.join(', '),
|
230
|
+
'Host' => host,
|
231
|
+
'Accept-Encoding' => "#{'br;q=1.0,' if brotli?}gzip;q=1.0,deflate;q=0.6,identity;q=0.3",
|
232
|
+
'Date' => Time.now.utc.httpdate
|
233
|
+
].tap do |headers|
|
234
|
+
headers.private_key = private_key
|
235
|
+
headers.public_key_url = public_key_url
|
236
|
+
end
|
124
237
|
end
|
125
238
|
|
126
|
-
#
|
239
|
+
# Generate a checksum for the request body
|
127
240
|
#
|
128
|
-
# @
|
241
|
+
# @param :body [String]
|
129
242
|
# @param :headers [Hash]
|
130
|
-
def
|
131
|
-
headers['
|
132
|
-
'keyId' => public_key_url,
|
133
|
-
'algorithm' => ALGORITHM,
|
134
|
-
'headers' => signable_headers(headers),
|
135
|
-
'signature' => signed_headers(headers)
|
136
|
-
}.map do |key, value|
|
137
|
-
"#{key}=\"#{value}\""
|
138
|
-
end.join(',')
|
139
|
-
|
140
|
-
headers.delete REQUEST_TARGET
|
243
|
+
def checksum_body!(body, headers)
|
244
|
+
headers['Digest'] = "SHA-256=#{Digest::SHA256.base64digest body}"
|
141
245
|
|
142
246
|
nil
|
143
247
|
end
|
144
248
|
|
145
|
-
#
|
146
|
-
#
|
249
|
+
# If the endpoint is on a subdirectory, we need the absolute
|
250
|
+
# path for signing
|
147
251
|
#
|
252
|
+
# @param :endpoint [String]
|
148
253
|
# @return [String]
|
149
|
-
def
|
150
|
-
|
254
|
+
def absolute_endpoint(endpoint)
|
255
|
+
"#{uri.path}/#{endpoint}".squeeze('/')
|
151
256
|
end
|
152
257
|
|
153
|
-
#
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
258
|
+
# Brotli available
|
259
|
+
def brotli?
|
260
|
+
begin
|
261
|
+
require 'brs'
|
262
|
+
rescue LoadError => e
|
263
|
+
if logger
|
264
|
+
logger.warn e.message
|
265
|
+
else
|
266
|
+
puts e
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
270
|
+
defined? ::BRS
|
164
271
|
end
|
165
272
|
|
166
|
-
#
|
273
|
+
# Inflates a Brotli compressed fragment. A fragment could be
|
274
|
+
# small enough to contain a huge inflated payload. In our
|
275
|
+
# tests, 2GB of zeroes were compressed to a 1.6KB fragment.
|
167
276
|
#
|
168
|
-
# @
|
169
|
-
# @
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
277
|
+
# @todo Maybe each_char is too slow?
|
278
|
+
# @param fragment [HTTParty::ResponseFragment]
|
279
|
+
# @param append_to [String]
|
280
|
+
def inflate_fragment!(fragment, append_to)
|
281
|
+
fragment_io = StringIO.new(fragment)
|
282
|
+
|
283
|
+
BRS::Stream::Reader.new(fragment_io, source_buffer_length: 256,
|
284
|
+
destination_buffer_length: 1024).each_char do |char|
|
285
|
+
append_to << char
|
286
|
+
|
287
|
+
raise ContentTooLargeError if append_to.bytesize > 1_000_000
|
288
|
+
end
|
174
289
|
end
|
175
290
|
|
176
|
-
#
|
291
|
+
# Collect fragments into body by checking the bytesize and
|
292
|
+
# failing if it gets too big.
|
177
293
|
#
|
178
|
-
# @param :
|
179
|
-
# @param :
|
180
|
-
def
|
181
|
-
|
294
|
+
# @param :fragment [HTTParty::ResponseFragment]
|
295
|
+
# @param :append_to [String]
|
296
|
+
def fragment_to_body!(fragment, append_to)
|
297
|
+
case fragment.http_response['content-encoding']
|
298
|
+
when 'br'
|
299
|
+
# Raise an error rather than returning an empty body
|
300
|
+
unless brotli?
|
301
|
+
raise BrotliUnsupportedError,
|
302
|
+
'Server sent brotli-encoded response but ruby-brs gem is missing'
|
303
|
+
end
|
182
304
|
|
183
|
-
|
305
|
+
inflate_fragment!(fragment, append_to)
|
306
|
+
else
|
307
|
+
append_to << fragment
|
308
|
+
end
|
309
|
+
|
310
|
+
raise ContentTooLargeError if append_to.bytesize > 1_000_000
|
311
|
+
rescue ContentTooLargeError
|
312
|
+
raise ContentTooLargeError, "Content too large #{append_to.bytesize} bytes!"
|
184
313
|
end
|
185
314
|
|
186
|
-
#
|
315
|
+
# Re-adds a streamed body to the response and caches it again so
|
316
|
+
# it can carry the body
|
187
317
|
#
|
188
|
-
# @param
|
189
|
-
# @param
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
318
|
+
# @param response [HTTParty::Response]
|
319
|
+
# @param body [String]
|
320
|
+
def fix_response!(response, body)
|
321
|
+
request = response.request
|
322
|
+
parser = request.options[:parser]
|
323
|
+
format = request.format || :plain
|
194
324
|
|
195
|
-
|
325
|
+
response.instance_variable_set(:@body, body)
|
326
|
+
response.instance_variable_set(:@parsed_block, -> { parser.call(body, format) })
|
327
|
+
response.instance_variable_set(:@parsed_response, nil)
|
328
|
+
|
329
|
+
self.class.cache_store.set(response.cache_key, response)
|
196
330
|
end
|
197
331
|
end
|
198
332
|
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'addressable'
|
4
|
+
require_relative 'reference'
|
5
|
+
require_relative 'referenced_object'
|
6
|
+
|
7
|
+
class DistributedPress
|
8
|
+
module V1
|
9
|
+
module Social
|
10
|
+
# Fetches ActivityStreams from different instances by
|
11
|
+
# instantiating clients.
|
12
|
+
class Dereferencer
|
13
|
+
# @return [DistributedPress::V1::Social::Client]
|
14
|
+
attr_reader :client
|
15
|
+
|
16
|
+
# We only need the client
|
17
|
+
def _dump(_)
|
18
|
+
Marshal.dump(client)
|
19
|
+
end
|
20
|
+
|
21
|
+
def self._load(client)
|
22
|
+
new(client: Marshal.load(client))
|
23
|
+
end
|
24
|
+
|
25
|
+
# @param :client [DistributedPress::V1::Social::Client]
|
26
|
+
def initialize(client:)
|
27
|
+
@client = client
|
28
|
+
@parser =
|
29
|
+
proc do |body, format|
|
30
|
+
next HTTParty::Parser.call(body, format || :plain) unless body&.starts_with? '{'
|
31
|
+
|
32
|
+
ReferencedObject.new(object: HTTParty::Parser.call(body, :json), dereferencer: self)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# Fetch a URI
|
37
|
+
#
|
38
|
+
# @param :uri [String, Addressable::URI]
|
39
|
+
# @return [HTTParty::Response]
|
40
|
+
def get(uri:)
|
41
|
+
uri = uris(uri)
|
42
|
+
|
43
|
+
clients(uri).get(endpoint: uri.path, parser: @parser)
|
44
|
+
end
|
45
|
+
|
46
|
+
# Gets a client for a URI
|
47
|
+
#
|
48
|
+
# @param :uri [Addressable::URI]
|
49
|
+
# @return [DistributedPress::V1::Social::Client]
|
50
|
+
def clients(uri)
|
51
|
+
@@clients ||= {}
|
52
|
+
@@clients[uri.origin] ||=
|
53
|
+
client.class.new(
|
54
|
+
url: uri.origin,
|
55
|
+
public_key_url: client.public_key_url,
|
56
|
+
private_key_pem: client.private_key.to_s,
|
57
|
+
logger: client.logger,
|
58
|
+
cache_store: client.class.cache_store
|
59
|
+
)
|
60
|
+
end
|
61
|
+
|
62
|
+
# Gets a reference for a URI and indexes it by the complete
|
63
|
+
# and normalized URI
|
64
|
+
#
|
65
|
+
# @param :uri [String, Addressable::URI]
|
66
|
+
# @return [DistributedPress::V1::Social::Reference]
|
67
|
+
def references(uri)
|
68
|
+
@@references ||= {}
|
69
|
+
@@references[uri.to_s] ||= Reference.new(uri: uri.to_s, dereferencer: self)
|
70
|
+
end
|
71
|
+
|
72
|
+
# Make sure we're getting a normalized Addressable::URI
|
73
|
+
#
|
74
|
+
# @param :uri [String, Addressable::URI]
|
75
|
+
# @return [Addressable::URI]
|
76
|
+
def uris(uri)
|
77
|
+
@@uris ||= {}
|
78
|
+
@@uris[uri.to_s] ||=
|
79
|
+
(if uri.is_a? Addressable::URI
|
80
|
+
uri
|
81
|
+
else
|
82
|
+
Addressable::URI.parse(uri)
|
83
|
+
end).normalize
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|