diamant 0.0.9 → 0.0.10
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/bin/diamant +2 -2
- data/lib/diamant/mimefile.rb +96 -0
- data/lib/diamant/response.rb +23 -14
- data/lib/diamant/version.rb +1 -1
- data/lib/diamant.rb +4 -8
- metadata +3 -3
- data/lib/diamant/mimetype.rb +0 -42
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3aae272eff26676b737dc65ff08ea76b0b4ea1822ec4785dfa47b97f0647dbe8
|
4
|
+
data.tar.gz: dc803566505cbed2653c44ac2bc7d4d3e0767ceb4604f91f32c1523917045192
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dc0d90505b8e7ef55f679c290aca6288dddd7c5b3208fbcab86dfa9501be7316ae8f425a3e5e4e5a3525d03c8765c397729231a590836bbe168783f162c3a852
|
7
|
+
data.tar.gz: b181654c75861b8a20308513b451999b63ce08b47c527ce2258de451daa16835aba9676c0f0ede70cb609eeb7b1a71f0a8f933b3c031ab39d505e4308396d532
|
data/bin/diamant
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
require 'diamant'
|
5
|
-
require 'diamant/cert_generator'
|
6
4
|
require 'optparse'
|
5
|
+
require_relative '../lib/diamant'
|
6
|
+
require_relative '../lib/diamant/cert_generator'
|
7
7
|
|
8
8
|
options = { hostname: '127.0.0.1', port: 1965 }
|
9
9
|
OptionParser.new do |parser| # rubocop:disable Metrics/BlockLength
|
@@ -0,0 +1,96 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Diamant
|
4
|
+
class MimeError < StandardError; end
|
5
|
+
|
6
|
+
# MIME aware wrapper for file
|
7
|
+
class MimeFile
|
8
|
+
attr_reader :body, :category, :content_type, :extension
|
9
|
+
|
10
|
+
MIMETYPES = {
|
11
|
+
'.gemini' => 'text/gemini',
|
12
|
+
'.gmi' => 'text/gemini',
|
13
|
+
'.txt' => 'text/plain',
|
14
|
+
'.md' => 'text/markdown',
|
15
|
+
'.org' => 'text/org',
|
16
|
+
'.rss' => 'application/rss+xml',
|
17
|
+
'.atom' => 'application/atom+xml',
|
18
|
+
'.xml' => 'application/xml',
|
19
|
+
'.svg' => 'image/svg+xml',
|
20
|
+
'.bmp' => 'image/bmp',
|
21
|
+
'.png' => 'image/png',
|
22
|
+
'.jpg' => 'image/jpeg',
|
23
|
+
'.jpeg' => 'image/jpeg',
|
24
|
+
'.gif' => 'image/gif',
|
25
|
+
'.webp' => 'image/webp',
|
26
|
+
'.mp3' => 'audio/mpeg',
|
27
|
+
'.ogg' => 'audio/ogg'
|
28
|
+
}.freeze
|
29
|
+
|
30
|
+
def initialize(path)
|
31
|
+
@path = path
|
32
|
+
@body = File.read path
|
33
|
+
@extension, @content_type = extract_info
|
34
|
+
@category = classify
|
35
|
+
validate!
|
36
|
+
prepare_gem_file if @content_type == 'text/gemini'
|
37
|
+
end
|
38
|
+
|
39
|
+
# Disable metrics on purpose for big switch
|
40
|
+
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/MethodLength
|
41
|
+
def validate
|
42
|
+
return true if @category == :text
|
43
|
+
|
44
|
+
case @content_type
|
45
|
+
when 'image/jpeg'
|
46
|
+
@body[0..1] == "\xFF\xD8"
|
47
|
+
when 'image/png'
|
48
|
+
@body[0..7] == "\x89PNG\r\n\u001A\n"
|
49
|
+
when 'image/gif'
|
50
|
+
@body[0..2] == 'GIF'
|
51
|
+
when 'image/webp'
|
52
|
+
@body[0..3] == 'RIFF' && @body[8..11] == 'WEBP'
|
53
|
+
when 'image/bmp'
|
54
|
+
@body[0..2] == 'BM'
|
55
|
+
when 'image/svg+xml', 'application/xml',
|
56
|
+
'application/rss+xml', 'application/atom+xml'
|
57
|
+
@body[0..5] == '<?xml '
|
58
|
+
when 'audio/mpeg'
|
59
|
+
@body[0..3] == 'ID3'
|
60
|
+
when 'audio/ogg'
|
61
|
+
@body[0..4] == 'OggS'
|
62
|
+
else
|
63
|
+
false
|
64
|
+
end
|
65
|
+
end
|
66
|
+
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/MethodLength
|
67
|
+
|
68
|
+
def validate!
|
69
|
+
return if validate
|
70
|
+
|
71
|
+
raise MimeError, "#{@path} does not looks like a #{@content_type} file!"
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
def extract_info
|
77
|
+
extension = File.extname @path
|
78
|
+
mime = MIMETYPES[extension]
|
79
|
+
raise MimeError, "#{@path} format is not supported!" if mime == ''
|
80
|
+
|
81
|
+
# Any other supported extension
|
82
|
+
[extension, mime]
|
83
|
+
end
|
84
|
+
|
85
|
+
def classify
|
86
|
+
return unless @content_type
|
87
|
+
|
88
|
+
@content_type.split('/', 2).first.to_sym
|
89
|
+
end
|
90
|
+
|
91
|
+
def prepare_gem_file
|
92
|
+
# Ensure each lines finishes with \r\n
|
93
|
+
@body = @body.each_line(chomp: true).to_a.join "\r\n"
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
data/lib/diamant/response.rb
CHANGED
@@ -3,9 +3,13 @@
|
|
3
3
|
require 'net/gemini/request'
|
4
4
|
require 'uri/gemini'
|
5
5
|
|
6
|
+
require_relative 'mimefile'
|
7
|
+
|
6
8
|
module Diamant
|
7
9
|
# Methods to generate requests responses
|
8
10
|
module Response
|
11
|
+
class NotFound < Net::Gemini::BadResponse; end
|
12
|
+
|
9
13
|
private
|
10
14
|
|
11
15
|
def reject_request?(sock, current_load)
|
@@ -25,34 +29,39 @@ module Diamant
|
|
25
29
|
true
|
26
30
|
end
|
27
31
|
|
28
|
-
def
|
32
|
+
def build_response(client)
|
29
33
|
r = Net::Gemini::Request.read_new(client)
|
30
|
-
|
34
|
+
file_path = route r.path
|
35
|
+
[r.uri, read_mime_file(file_path)]
|
36
|
+
rescue NotFound => e
|
37
|
+
@logger.warn "51 - #{e}"
|
38
|
+
[r.uri, "51 Not found!\r\n"]
|
31
39
|
rescue Net::Gemini::BadRequest, URI::InvalidURIError => e
|
32
40
|
@logger.error "59 - #{e}"
|
33
|
-
[nil,
|
41
|
+
[nil, "59\r\n"]
|
34
42
|
end
|
35
43
|
|
36
44
|
def route(path)
|
37
45
|
# In any case, remove the / prefix
|
38
|
-
route = File.expand_path path.delete_prefix('/')
|
46
|
+
route = File.expand_path path.delete_prefix('/')
|
39
47
|
# We better should use some sort of chroot...
|
40
48
|
unless route.start_with?(Dir.pwd)
|
41
|
-
|
42
|
-
return ['51 Not found!']
|
49
|
+
raise NotFound, "Attempt to get something out of public_dir: #{route}"
|
43
50
|
end
|
51
|
+
|
44
52
|
route << '/index.gmi' if File.directory?(route)
|
45
|
-
return
|
53
|
+
return route if File.exist?(route)
|
46
54
|
|
47
|
-
|
55
|
+
raise NotFound, "File #{route} does not exist"
|
48
56
|
end
|
49
57
|
|
50
|
-
def
|
51
|
-
info = Diamant::
|
52
|
-
|
53
|
-
|
54
|
-
rescue Diamant::MimeError
|
55
|
-
|
58
|
+
def read_mime_file(route)
|
59
|
+
info = Diamant::MimeFile.new route
|
60
|
+
header = "20 #{info.content_type}"
|
61
|
+
[header, info.body].join("\r\n")
|
62
|
+
rescue Diamant::MimeError => e
|
63
|
+
@logger.error "50 - #{e}"
|
64
|
+
"50 Not a supported file!\r\n"
|
56
65
|
end
|
57
66
|
end
|
58
67
|
end
|
data/lib/diamant/version.rb
CHANGED
data/lib/diamant.rb
CHANGED
@@ -6,9 +6,7 @@ require 'English'
|
|
6
6
|
require 'openssl'
|
7
7
|
require 'fileutils'
|
8
8
|
|
9
|
-
|
10
|
-
require 'diamant/mimetype'
|
11
|
-
require 'diamant/response'
|
9
|
+
require_relative 'diamant/response'
|
12
10
|
|
13
11
|
module Diamant
|
14
12
|
# Runs the server request/answer loop.
|
@@ -62,16 +60,14 @@ module Diamant
|
|
62
60
|
end
|
63
61
|
|
64
62
|
def handle_client(client)
|
65
|
-
current_load = Thread.list.length - 1
|
63
|
+
current_load = Thread.list.length - 1 # Exclude main thread
|
66
64
|
return if reject_request?(client, current_load)
|
67
65
|
|
68
|
-
uri, answer =
|
66
|
+
uri, answer = build_response(client)
|
69
67
|
log_line = [current_load, client.peeraddr[3], answer[0]]
|
70
68
|
log_line << uri if uri
|
71
69
|
@logger.info log_line.join(' - ')
|
72
|
-
|
73
|
-
client.puts "#{line}\r\n"
|
74
|
-
end
|
70
|
+
client.puts answer
|
75
71
|
end
|
76
72
|
|
77
73
|
def ssl_context
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: diamant
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.10
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Étienne Deparis
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-07-
|
11
|
+
date: 2024-07-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: ruby-net-text
|
@@ -38,7 +38,7 @@ files:
|
|
38
38
|
- bin/diamant
|
39
39
|
- lib/diamant.rb
|
40
40
|
- lib/diamant/cert_generator.rb
|
41
|
-
- lib/diamant/
|
41
|
+
- lib/diamant/mimefile.rb
|
42
42
|
- lib/diamant/response.rb
|
43
43
|
- lib/diamant/version.rb
|
44
44
|
homepage: https://git.umaneti.net/diamant/about/
|
data/lib/diamant/mimetype.rb
DELETED
@@ -1,42 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Diamant
|
4
|
-
class MimeError < StandardError; end
|
5
|
-
|
6
|
-
# Helper to understand what mimetype has a given file
|
7
|
-
class MimeType
|
8
|
-
attr_reader :extension, :content_type
|
9
|
-
|
10
|
-
MIMETYPES = {
|
11
|
-
'.gemini' => 'text/gemini',
|
12
|
-
'.gmi' => 'text/gemini',
|
13
|
-
'.txt' => 'text/plain',
|
14
|
-
'.md' => 'text/markdown',
|
15
|
-
'.org' => 'text/org',
|
16
|
-
'.xml' => 'application/xml',
|
17
|
-
'.png' => 'image/png',
|
18
|
-
'.jpg' => 'image/jpeg',
|
19
|
-
'.jpeg' => 'image/jpeg',
|
20
|
-
'.gif' => 'image/gif'
|
21
|
-
}.freeze
|
22
|
-
|
23
|
-
def initialize(path)
|
24
|
-
@path = path
|
25
|
-
extract_info
|
26
|
-
end
|
27
|
-
|
28
|
-
def supported?
|
29
|
-
@extension != '' && MIMETYPES.has_key?(@extension)
|
30
|
-
end
|
31
|
-
|
32
|
-
private
|
33
|
-
|
34
|
-
def extract_info
|
35
|
-
@extension = File.extname @path
|
36
|
-
raise MimeError, "#{@path} format is not supported!" unless supported?
|
37
|
-
|
38
|
-
# Any other supported extension
|
39
|
-
@content_type = MIMETYPES[@extension]
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|