ruby-net-text 0.0.9 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 866ba253d1dbbc5b2e9a8eba1f6bf4813c6f3b163ce3278d36c4932be80c3714
4
- data.tar.gz: 16e0059f252a8d88c8c04667e1b779e65eadae7c1c6da17aba4487fdac459070
3
+ metadata.gz: a5e45e34116b3865c72a1691baa06372be3266aebdae1fc7d823ba4136d4438a
4
+ data.tar.gz: 3e8045ac8d2b7c82b4dfffccaeeda707d92aded2f36b7eff794008d0ec8e5b06
5
5
  SHA512:
6
- metadata.gz: 4643771216d1ee0c494a5e368037fbfcfcf303a290dfa2260af3cb9125cbdbeaad6d4fc7ee3836abb914e5c5f731d4f351fb1e4e19193751b07bfd31a0385abe
7
- data.tar.gz: 1625097ffa76cc274460e64987fca75cb43db9963d4401e434a796b4aa7effdbbbda9e787001597183ce4d8e91db15f0c0c8f5f8945b725485aef218b314ab4d
6
+ metadata.gz: cbd55419137361ce17b1df2459a734fbd305fad146701fd37b20df22e7fe861daead41494c7e20ce76baf2d37c825667acc87cebe49ef1310001f38b703f1152
7
+ data.tar.gz: 6cc15cc19dc559ed63b092f9c1c0c6622685f6a07d90a5634b9f074ca02e762bc4c60be95e8f6d45192718c2797b039e5ea3b13030d52d16b7c585e72b628065
data/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2020 Étienne Deparis
3
+ Copyright (c) 2020 Étienne Pflieger
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
@@ -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,29 @@ 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
+ next if opt.empty?
15
+
16
+ key = opt[0].downcase.to_sym
17
+ next unless mime.has_key? key
18
+
19
+ [key, opt[1].downcase]
20
+ end
21
+ mime.merge new_mime.to_h
22
+ end
23
+
10
24
  def parse_meta
11
25
  header = { status: @status, meta: @meta, mimetype: nil }
12
26
  return header unless body_permitted?
13
- mime = { lang: nil, charset: 'utf-8', format: nil }
27
+
14
28
  raw_meta = meta.split(';').map(&:strip)
15
29
  header[:mimetype] = raw_meta.shift
16
30
  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)
31
+
32
+ header.merge received_mime(raw_meta)
23
33
  end
24
34
 
25
35
  def parse_preformatted_block(line, buf)
@@ -36,6 +46,7 @@ module Net
36
46
  def parse_link(line)
37
47
  m = line.strip.match(/\A=>\s*([^\s]+)(?:\s*(.+))?\z/)
38
48
  return if m.nil?
49
+
39
50
  begin
40
51
  uri = URI(m[1])
41
52
  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,17 +1,17 @@
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.1
5
5
  platform: ruby
6
6
  authors:
7
- - Étienne Deparis
7
+ - Étienne Pflieger
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-10-20 00:00:00.000000000 Z
11
+ date: 2024-09-22 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description:
14
- email: etienne@depar.is
14
+ email: etienne@pflieger.bzh
15
15
  executables: []
16
16
  extensions: []
17
17
  extra_rdoc_files: []
@@ -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.19
67
67
  signing_key:
68
68
  specification_version: 4
69
69
  summary: Finger, Gemini, Gopher and Nex support for Net::* and URI::*