ruby-net-text 0.0.9 → 0.1.0
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/net/gemini/client/ssl.rb +7 -4
- data/lib/net/gemini/client.rb +14 -3
- data/lib/net/gemini/request.rb +5 -4
- data/lib/net/gemini/response/parser.rb +16 -7
- data/lib/net/gemini/response.rb +14 -5
- data/lib/net/gemini.rb +2 -3
- data/lib/net/text/generic.rb +1 -0
- data/lib/net/text/reflow.rb +3 -7
- data/lib/uri/finger.rb +3 -1
- data/lib/uri/gemini.rb +2 -3
- data/lib/uri/gopher.rb +4 -2
- data/lib/uri/nex.rb +2 -2
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2610c775fb8178c5c035a7ffbb9176ba679a15fcd061fed7e192b3d83b609727
|
4
|
+
data.tar.gz: 386863cda1e5682d05bc75a52da55ae4d972393db7efa38fed7d259b44f9e717
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8c7663972f81f9f93e02bf76bccfaa1b158b4034259617e94856d27fb63aa5368def164135cea3318c355e3e137ae0c6b852b96194c52db9180aecd318a8d3e1
|
7
|
+
data.tar.gz: 61bff20043c1866c3075312a87f9a68dc7d7aa7fd869d8962672dc2ce6eeed9ad065d6ca0bc8330bc94a41fa041933a7bf0da20c0871826d805a0febf2d8dcb2
|
@@ -23,6 +23,7 @@ module Net
|
|
23
23
|
|
24
24
|
cert_file = File.expand_path("#{@certs_path}/#{@host}.pem")
|
25
25
|
return ssl_check_existing(cert, cert_file) if File.exist?(cert_file)
|
26
|
+
|
26
27
|
FileUtils.mkdir_p(File.expand_path(@certs_path))
|
27
28
|
File.open(cert_file, 'wb') { |f| f.print cert.to_pem }
|
28
29
|
true
|
@@ -30,10 +31,12 @@ module Net
|
|
30
31
|
|
31
32
|
def ssl_context
|
32
33
|
ssl_context = OpenSSL::SSL::SSLContext.new
|
33
|
-
ssl_context.set_params(
|
34
|
-
|
35
|
-
|
36
|
-
|
34
|
+
ssl_context.set_params(
|
35
|
+
verify_mode: OpenSSL::SSL::VERIFY_PEER,
|
36
|
+
min_version: OpenSSL::SSL::TLS1_2_VERSION,
|
37
|
+
ca_file: '/etc/ssl/certs/ca-certificates.crt',
|
38
|
+
verify_hostname: true
|
39
|
+
)
|
37
40
|
ssl_context.verify_callback = lambda do |preverify_ok, store_context|
|
38
41
|
return true if preverify_ok
|
39
42
|
|
data/lib/net/gemini/client.rb
CHANGED
@@ -3,9 +3,11 @@
|
|
3
3
|
require_relative 'error'
|
4
4
|
require_relative 'request'
|
5
5
|
require_relative 'response'
|
6
|
+
require_relative '../text/generic'
|
6
7
|
|
7
8
|
module Net
|
8
|
-
module Gemini
|
9
|
+
module Gemini # rubocop:disable Style/Documentation
|
10
|
+
# An example client to fetch resources hosted on Gemini network.
|
9
11
|
class Client
|
10
12
|
attr_writer :certs_path
|
11
13
|
|
@@ -15,13 +17,18 @@ module Net
|
|
15
17
|
@certs_path = '~/.cache/gemini/certs'
|
16
18
|
end
|
17
19
|
|
18
|
-
|
20
|
+
# This method can raise an OpenSSL::SSL::SSLError
|
21
|
+
def request!(uri)
|
19
22
|
init_sockets
|
20
23
|
req = Request.new uri
|
21
24
|
req.write @ssl_socket
|
22
25
|
res = Response.read_new(@ssl_socket)
|
23
26
|
res.uri = uri
|
24
27
|
res.reading_body(@ssl_socket)
|
28
|
+
end
|
29
|
+
|
30
|
+
def request(uri)
|
31
|
+
request! uri
|
25
32
|
rescue OpenSSL::SSL::SSLError => e
|
26
33
|
msg = format(
|
27
34
|
'SSLError: %<cause>s',
|
@@ -36,8 +43,10 @@ module Net
|
|
36
43
|
|
37
44
|
def fetch(uri, limit = 5)
|
38
45
|
raise Error, 'Too many Gemini redirects' if limit.zero?
|
46
|
+
|
39
47
|
r = request(uri)
|
40
48
|
return r unless r.status[0] == '3'
|
49
|
+
|
41
50
|
begin
|
42
51
|
uri = handle_redirect(r)
|
43
52
|
rescue ArgumentError, URI::InvalidURIError
|
@@ -55,6 +64,7 @@ module Net
|
|
55
64
|
new_uri = URI(response.meta)
|
56
65
|
uri.merge!(new_uri)
|
57
66
|
raise Error, "Redirect loop on #{uri}" if uri.to_s == old_url
|
67
|
+
|
58
68
|
@host = uri.host
|
59
69
|
@port = uri.port
|
60
70
|
uri
|
@@ -78,7 +88,8 @@ module Net
|
|
78
88
|
start(uri.host, uri.port) { |gem| gem.fetch(uri) }
|
79
89
|
end
|
80
90
|
|
81
|
-
def self.get(
|
91
|
+
def self.get(string_or_uri)
|
92
|
+
uri = Net::Text::Generic.build_uri string_or_uri, URI::Gemini
|
82
93
|
get_response(uri).body
|
83
94
|
end
|
84
95
|
end
|
data/lib/net/gemini/request.rb
CHANGED
@@ -13,7 +13,7 @@ module Net
|
|
13
13
|
# The syntax of Gemini Requests are defined in the Gemini
|
14
14
|
# specification, section 2.
|
15
15
|
#
|
16
|
-
# @see https://
|
16
|
+
# @see https://geminiprotocol.net/docs/protocol-specification.html
|
17
17
|
#
|
18
18
|
class Request
|
19
19
|
attr_reader :uri
|
@@ -21,11 +21,11 @@ module Net
|
|
21
21
|
def initialize(uri_or_str)
|
22
22
|
# In any case, make some sanity check over this uri-like think
|
23
23
|
url = uri_or_str.to_s
|
24
|
-
if url.length > 1024
|
25
|
-
|
26
|
-
end
|
24
|
+
raise BadRequest, "Request too long: #{url.dump}" if url.length > 1024
|
25
|
+
|
27
26
|
@uri = URI(url)
|
28
27
|
return if uri.is_a? URI::Gemini
|
28
|
+
|
29
29
|
raise BadRequest, "Not a Gemini URI: #{url.dump}"
|
30
30
|
end
|
31
31
|
|
@@ -45,6 +45,7 @@ module Net
|
|
45
45
|
str = sock.gets($INPUT_RECORD_SEPARATOR, 1026)
|
46
46
|
m = /\A(.*)\r\n\z/.match(str)
|
47
47
|
raise BadRequest, "Malformed request: #{str&.dump}" if m.nil?
|
48
|
+
|
48
49
|
new(m[1])
|
49
50
|
end
|
50
51
|
end
|
@@ -7,19 +7,27 @@ module Net
|
|
7
7
|
class Response
|
8
8
|
private
|
9
9
|
|
10
|
+
def received_mime(raw_meta)
|
11
|
+
meta_data = raw_meta.map { |m| m.split('=') }
|
12
|
+
mime = { lang: nil, charset: 'utf-8', format: nil }
|
13
|
+
new_mime = meta_data.filter_map do |opt|
|
14
|
+
key = opt[0].downcase.to_sym
|
15
|
+
next unless mime.has_key? key
|
16
|
+
|
17
|
+
[key, opt[1].downcase]
|
18
|
+
end
|
19
|
+
mime.merge new_mime.to_h
|
20
|
+
end
|
21
|
+
|
10
22
|
def parse_meta
|
11
23
|
header = { status: @status, meta: @meta, mimetype: nil }
|
12
24
|
return header unless body_permitted?
|
13
|
-
|
25
|
+
|
14
26
|
raw_meta = meta.split(';').map(&:strip)
|
15
27
|
header[:mimetype] = raw_meta.shift
|
16
28
|
return header unless raw_meta.any?
|
17
|
-
|
18
|
-
|
19
|
-
next unless mime.has_key? key
|
20
|
-
mime[key] = opt[1].downcase
|
21
|
-
end
|
22
|
-
header.merge(mime)
|
29
|
+
|
30
|
+
header.merge received_mime(raw_meta)
|
23
31
|
end
|
24
32
|
|
25
33
|
def parse_preformatted_block(line, buf)
|
@@ -36,6 +44,7 @@ module Net
|
|
36
44
|
def parse_link(line)
|
37
45
|
m = line.strip.match(/\A=>\s*([^\s]+)(?:\s*(.+))?\z/)
|
38
46
|
return if m.nil?
|
47
|
+
|
39
48
|
begin
|
40
49
|
uri = URI(m[1])
|
41
50
|
rescue URI::InvalidURIError
|
data/lib/net/gemini/response.rb
CHANGED
@@ -12,7 +12,7 @@ module Net
|
|
12
12
|
# The syntax of Gemini Responses are defined in the Gemini
|
13
13
|
# specification, section 3.
|
14
14
|
#
|
15
|
-
# @see https://
|
15
|
+
# @see https://geminiprotocol.net/docs/protocol-specification.html
|
16
16
|
#
|
17
17
|
# See {Net::Gemini} documentation to see how to interract with a
|
18
18
|
# Response.
|
@@ -55,12 +55,12 @@ module Net
|
|
55
55
|
|
56
56
|
def reading_body(sock)
|
57
57
|
return self unless body_permitted?
|
58
|
+
|
58
59
|
raw_body = []
|
59
|
-
|
60
|
-
raw_body << line
|
61
|
-
end
|
60
|
+
sock.each_line { raw_body << _1 }
|
62
61
|
@body = encode_body(raw_body.join)
|
63
62
|
return self unless @header[:mimetype] == 'text/gemini'
|
63
|
+
|
64
64
|
parse_body
|
65
65
|
self
|
66
66
|
end
|
@@ -72,7 +72,14 @@ module Net
|
|
72
72
|
# @return [String] the body content
|
73
73
|
def body(reflow_at: -1)
|
74
74
|
return '' if @body.nil? # Maybe not ready?
|
75
|
-
|
75
|
+
|
76
|
+
unless reflow_at.is_a? Integer
|
77
|
+
raise(
|
78
|
+
ArgumentError, "reflow_at must be Integer, #{reflow_at.class} given"
|
79
|
+
)
|
80
|
+
end
|
81
|
+
|
82
|
+
return @body if reflow_at <= 0 || @header[:format] == 'fixed'
|
76
83
|
|
77
84
|
Net::Text::Reflow.format_body(@body, reflow_at)
|
78
85
|
end
|
@@ -86,6 +93,7 @@ module Net
|
|
86
93
|
str = sock.gets($INPUT_RECORD_SEPARATOR, 1029)
|
87
94
|
m = /\A([1-6]\d) (.*)\r\n\z/.match(str)
|
88
95
|
raise BadResponse, "wrong status line: #{str.dump}" if m.nil?
|
96
|
+
|
89
97
|
new(*m.captures)
|
90
98
|
end
|
91
99
|
end
|
@@ -94,6 +102,7 @@ module Net
|
|
94
102
|
|
95
103
|
def encode_body(body)
|
96
104
|
return body unless @header[:mimetype].start_with?('text/')
|
105
|
+
|
97
106
|
if @header[:charset] && @header[:charset] != 'utf-8'
|
98
107
|
# If body use another charset than utf-8, we need first to
|
99
108
|
# declare the raw byte string as using this chasret
|
data/lib/net/gemini.rb
CHANGED
@@ -11,7 +11,7 @@ module Net
|
|
11
11
|
#
|
12
12
|
# Net::Gemini provides a library which can be used to build Gemini
|
13
13
|
# user-agents.
|
14
|
-
# @see
|
14
|
+
# @see gemini://geminiprotocol.net/docs/specification.gmi
|
15
15
|
#
|
16
16
|
# Net::Gemini is designed to work closely with URI.
|
17
17
|
#
|
@@ -29,8 +29,7 @@ module Net
|
|
29
29
|
#
|
30
30
|
# === GET by URI
|
31
31
|
#
|
32
|
-
#
|
33
|
-
# Net::Gemini.get(uri) # => String
|
32
|
+
# Net::Gemini.get('gemini://gemini.circumlunar.space/') # => String
|
34
33
|
#
|
35
34
|
# === GET with Dynamic Parameters
|
36
35
|
#
|
data/lib/net/text/generic.rb
CHANGED
@@ -19,6 +19,7 @@ module Net
|
|
19
19
|
def self.build_uri(string_or_uri, uri_class)
|
20
20
|
string_or_uri = URI(string_or_uri) if string_or_uri.is_a?(String)
|
21
21
|
return string_or_uri if string_or_uri.is_a?(uri_class)
|
22
|
+
|
22
23
|
raise ArgumentError, "uri is not a String, nor an #{uri_class.name}"
|
23
24
|
end
|
24
25
|
end
|
data/lib/net/text/reflow.rb
CHANGED
@@ -12,6 +12,7 @@ module Net
|
|
12
12
|
return '' unless m
|
13
13
|
# Each quote line should begin with the quote mark
|
14
14
|
return m[1] if m[1].start_with?('>')
|
15
|
+
|
15
16
|
' ' * m[1].length
|
16
17
|
end
|
17
18
|
|
@@ -20,6 +21,7 @@ module Net
|
|
20
21
|
if mono_block_open || line.start_with?('=>') || line.length < length
|
21
22
|
return [line]
|
22
23
|
end
|
24
|
+
|
23
25
|
output = []
|
24
26
|
prefix = reflow_line_prefix(line)
|
25
27
|
limit_chars = ['-', '', ' '].freeze
|
@@ -35,15 +37,9 @@ module Net
|
|
35
37
|
end
|
36
38
|
|
37
39
|
def self.format_body(body, length)
|
38
|
-
unless length.is_a? Integer
|
39
|
-
raise ArgumentError, "Length must be Integer, #{length.class} given"
|
40
|
-
end
|
41
|
-
return body if length.zero?
|
42
|
-
|
43
40
|
new_body = []
|
44
41
|
mono_block_open = false
|
45
|
-
|
46
|
-
while (line = buf.gets)
|
42
|
+
body.each_line do |line|
|
47
43
|
if line.start_with?('```')
|
48
44
|
mono_block_open = !mono_block_open
|
49
45
|
# Don't include code block toggle lines
|
data/lib/uri/finger.rb
CHANGED
@@ -14,13 +14,15 @@ module URI # :nodoc:
|
|
14
14
|
DEFAULT_PORT = 79
|
15
15
|
|
16
16
|
# An Array of the available components for URI::Finger.
|
17
|
-
COMPONENT = [
|
17
|
+
COMPONENT = %i[scheme userinfo host port path].freeze
|
18
18
|
|
19
19
|
def name
|
20
20
|
# Utilitary method to extract a name to query, either from the userinfo
|
21
21
|
# part or from the path.
|
22
22
|
return @user if @user
|
23
|
+
|
23
24
|
return '' unless @path
|
25
|
+
|
24
26
|
# Remove leading /
|
25
27
|
@path[1..] || ''
|
26
28
|
end
|
data/lib/uri/gemini.rb
CHANGED
@@ -7,15 +7,14 @@ module URI # :nodoc:
|
|
7
7
|
# The syntax of Gemini URIs is defined in the Gemini specification,
|
8
8
|
# section 1.2.
|
9
9
|
#
|
10
|
-
# @see https://
|
10
|
+
# @see https://geminiprotocol.net/docs/protocol-specification.html
|
11
11
|
#
|
12
12
|
class Gemini < HTTP
|
13
13
|
# A Default port of 1965 for URI::Gemini.
|
14
14
|
DEFAULT_PORT = 1965
|
15
15
|
|
16
16
|
# An Array of the available components for URI::Gemini.
|
17
|
-
COMPONENT = [
|
18
|
-
:path, :query, :fragment].freeze
|
17
|
+
COMPONENT = %i[scheme host port path query fragment].freeze
|
19
18
|
end
|
20
19
|
|
21
20
|
if respond_to? :register_scheme
|
data/lib/uri/gopher.rb
CHANGED
@@ -14,22 +14,24 @@ module URI # :nodoc:
|
|
14
14
|
DEFAULT_PORT = 70
|
15
15
|
|
16
16
|
# An Array of the available components for URI::Gopher.
|
17
|
-
COMPONENT = [
|
17
|
+
COMPONENT = %i[scheme host port path].freeze
|
18
18
|
|
19
19
|
def selector
|
20
20
|
return @selector if defined? @selector
|
21
|
+
|
21
22
|
@selector = extract_gopher_path_elements[1]
|
22
23
|
end
|
23
24
|
|
24
25
|
def item_type
|
25
26
|
return @item_type if defined? @item_type
|
27
|
+
|
26
28
|
@item_type = extract_gopher_path_elements[0]
|
27
29
|
end
|
28
30
|
|
29
31
|
private
|
30
32
|
|
31
33
|
def extract_gopher_path_elements
|
32
|
-
m =
|
34
|
+
m = %r{\A/([0-9+dfghisA-LT])(/.*)?\Z}.match(@path)
|
33
35
|
m.captures
|
34
36
|
end
|
35
37
|
end
|
data/lib/uri/nex.rb
CHANGED
@@ -7,14 +7,14 @@ module URI # :nodoc:
|
|
7
7
|
# The syntax of Nex URIs is defined in the Nex specification,
|
8
8
|
# section 1.2.
|
9
9
|
#
|
10
|
-
# @see
|
10
|
+
# @see nex://nightfall.city/nex/info/specification.txt
|
11
11
|
#
|
12
12
|
class Nex < HTTP
|
13
13
|
# A Default port of 1900 for URI::Nex.
|
14
14
|
DEFAULT_PORT = 1900
|
15
15
|
|
16
16
|
# An Array of the available components for URI::Nex.
|
17
|
-
COMPONENT = [
|
17
|
+
COMPONENT = %i[scheme host port path].freeze
|
18
18
|
end
|
19
19
|
|
20
20
|
if respond_to? :register_scheme
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby-net-text
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Étienne Deparis
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-07-13 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description:
|
14
14
|
email: etienne@depar.is
|
@@ -63,7 +63,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
63
63
|
- !ruby/object:Gem::Version
|
64
64
|
version: '0'
|
65
65
|
requirements: []
|
66
|
-
rubygems_version: 3.
|
66
|
+
rubygems_version: 3.5.15
|
67
67
|
signing_key:
|
68
68
|
specification_version: 4
|
69
69
|
summary: Finger, Gemini, Gopher and Nex support for Net::* and URI::*
|