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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 866ba253d1dbbc5b2e9a8eba1f6bf4813c6f3b163ce3278d36c4932be80c3714
4
- data.tar.gz: 16e0059f252a8d88c8c04667e1b779e65eadae7c1c6da17aba4487fdac459070
3
+ metadata.gz: 2610c775fb8178c5c035a7ffbb9176ba679a15fcd061fed7e192b3d83b609727
4
+ data.tar.gz: 386863cda1e5682d05bc75a52da55ae4d972393db7efa38fed7d259b44f9e717
5
5
  SHA512:
6
- metadata.gz: 4643771216d1ee0c494a5e368037fbfcfcf303a290dfa2260af3cb9125cbdbeaad6d4fc7ee3836abb914e5c5f731d4f351fb1e4e19193751b07bfd31a0385abe
7
- data.tar.gz: 1625097ffa76cc274460e64987fca75cb43db9963d4401e434a796b4aa7effdbbbda9e787001597183ce4d8e91db15f0c0c8f5f8945b725485aef218b314ab4d
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(verify_mode: OpenSSL::SSL::VERIFY_PEER)
34
- ssl_context.min_version = OpenSSL::SSL::TLS1_2_VERSION
35
- ssl_context.verify_hostname = true
36
- ssl_context.ca_file = '/etc/ssl/certs/ca-certificates.crt'
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
 
@@ -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
- def request(uri)
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(uri)
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
@@ -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://gemini.circumlunar.space/docs/specification.html
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
- raise BadRequest, "Request too long: #{url.dump}"
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
- mime = { lang: nil, charset: 'utf-8', format: nil }
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
- raw_meta.map { |m| m.split('=') }.each do |opt|
18
- key = opt[0].downcase.to_sym
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
@@ -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://gemini.circumlunar.space/docs/specification.html
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
- while (line = sock.gets)
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
- return @body if reflow_at < 0 || @header[:format] == 'fixed'
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 https://gemini.circumlunar.space/docs/specification.html
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
- # uri = URI('gemini://gemini.circumlunar.space/')
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
  #
@@ -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
@@ -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
- buf = StringIO.new(body)
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 = [:scheme, :userinfo, :host, :port, :path].freeze
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://gemini.circumlunar.space/docs/specification.html
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 = [:scheme, :host, :port,
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 = [:scheme, :host, :port, :path].freeze
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 = /\A\/([0-9+dfghisA-LT])(\/.*)?\Z/.match(@path)
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 https://gemini.circumlunar.space/docs/specification.html
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 = [:scheme, :host, :port, :path].freeze
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.9
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: 2023-10-20 00:00:00.000000000 Z
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.4.20
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::*