Add packages/ruby/server.rb as a git submodule
This commit is contained in:
parent
d71267970f
commit
337a2460e3
22 changed files with 81 additions and 386 deletions
3
.gitmodules
vendored
3
.gitmodules
vendored
|
@ -1,3 +1,6 @@
|
||||||
[submodule "submodules/surah-name-glyphs"]
|
[submodule "submodules/surah-name-glyphs"]
|
||||||
path = submodules/surah-name-glyphs
|
path = submodules/surah-name-glyphs
|
||||||
url = https://github.com/ReflectsLight/surah-name-glyphs
|
url = https://github.com/ReflectsLight/surah-name-glyphs
|
||||||
|
[submodule "packages/ruby/server.rb"]
|
||||||
|
path = packages/ruby/server.rb
|
||||||
|
url = https://github.com/0x1eef/server.rb
|
||||||
|
|
1
packages/ruby/server.rb
Submodule
1
packages/ruby/server.rb
Submodule
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit bf94e6f20c1f48ec002611912a53e0bf746f076f
|
8
packages/ruby/server/.gitignore
vendored
8
packages/ruby/server/.gitignore
vendored
|
@ -1,8 +0,0 @@
|
||||||
build/
|
|
||||||
tmp/
|
|
||||||
node_modules/
|
|
||||||
.bundle/
|
|
||||||
*.log
|
|
||||||
*.sh
|
|
||||||
*.core
|
|
||||||
Gemfile.lock
|
|
|
@ -1,40 +0,0 @@
|
||||||
##
|
|
||||||
# Plugins
|
|
||||||
require:
|
|
||||||
- standard
|
|
||||||
|
|
||||||
##
|
|
||||||
# Defaults: standard-rb
|
|
||||||
inherit_gem:
|
|
||||||
standard: config/base.yml
|
|
||||||
|
|
||||||
##
|
|
||||||
# All cops
|
|
||||||
AllCops:
|
|
||||||
TargetRubyVersion: 3.2
|
|
||||||
Include:
|
|
||||||
- lib/*.rb
|
|
||||||
- lib/**/*.rb
|
|
||||||
- test/*_test.rb
|
|
||||||
|
|
||||||
##
|
|
||||||
# Enabled
|
|
||||||
Style/FrozenStringLiteralComment:
|
|
||||||
Enabled: true
|
|
||||||
|
|
||||||
##
|
|
||||||
# Disabled
|
|
||||||
Layout/ArgumentAlignment:
|
|
||||||
Enabled: false
|
|
||||||
Layout/MultilineMethodCallIndentation:
|
|
||||||
Enabled: false
|
|
||||||
Layout/EmptyLineBetweenDefs:
|
|
||||||
Enabled: false
|
|
||||||
Style/TrivialAccessors:
|
|
||||||
Enabled: false
|
|
||||||
Lint/NestedMethodDefinition:
|
|
||||||
Exclude:
|
|
||||||
- test/server_dir_test.rb
|
|
||||||
Style/SingleLineMethods:
|
|
||||||
Exclude:
|
|
||||||
- test/server_dir_test.rb
|
|
|
@ -1,4 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
source "https://rubygems.org"
|
|
||||||
gemspec
|
|
77
packages/ruby/server/Gemfile.lock
Normal file
77
packages/ruby/server/Gemfile.lock
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
PATH
|
||||||
|
remote: .
|
||||||
|
specs:
|
||||||
|
server.rb (0.1.0)
|
||||||
|
json (= 2.6.1)
|
||||||
|
puma (~> 6.3)
|
||||||
|
racc (= 1.6.0)
|
||||||
|
rack (~> 3.0)
|
||||||
|
set (= 1.0.2)
|
||||||
|
stringio (= 3.0.1)
|
||||||
|
|
||||||
|
GEM
|
||||||
|
remote: https://rubygems.org/
|
||||||
|
specs:
|
||||||
|
ast (2.4.2)
|
||||||
|
json (2.6.1)
|
||||||
|
language_server-protocol (3.17.0.3)
|
||||||
|
lint_roller (1.0.0)
|
||||||
|
nio4r (2.5.9)
|
||||||
|
parallel (1.23.0)
|
||||||
|
parser (3.2.2.3)
|
||||||
|
ast (~> 2.4.1)
|
||||||
|
racc
|
||||||
|
power_assert (2.0.3)
|
||||||
|
puma (6.3.0)
|
||||||
|
nio4r (~> 2.0)
|
||||||
|
racc (1.6.0)
|
||||||
|
rack (3.0.8)
|
||||||
|
rack-test (2.1.0)
|
||||||
|
rack (>= 1.3)
|
||||||
|
rainbow (3.1.1)
|
||||||
|
regexp_parser (2.8.1)
|
||||||
|
rexml (3.2.5)
|
||||||
|
rubocop (1.52.1)
|
||||||
|
json (~> 2.3)
|
||||||
|
parallel (~> 1.10)
|
||||||
|
parser (>= 3.2.2.3)
|
||||||
|
rainbow (>= 2.2.2, < 4.0)
|
||||||
|
regexp_parser (>= 1.8, < 3.0)
|
||||||
|
rexml (>= 3.2.5, < 4.0)
|
||||||
|
rubocop-ast (>= 1.28.0, < 2.0)
|
||||||
|
ruby-progressbar (~> 1.7)
|
||||||
|
unicode-display_width (>= 2.4.0, < 3.0)
|
||||||
|
rubocop-ast (1.29.0)
|
||||||
|
parser (>= 3.2.1.0)
|
||||||
|
rubocop-performance (1.18.0)
|
||||||
|
rubocop (>= 1.7.0, < 2.0)
|
||||||
|
rubocop-ast (>= 0.4.0)
|
||||||
|
ruby-progressbar (1.13.0)
|
||||||
|
set (1.0.2)
|
||||||
|
standard (1.29.0)
|
||||||
|
language_server-protocol (~> 3.17.0.2)
|
||||||
|
lint_roller (~> 1.0)
|
||||||
|
rubocop (~> 1.52.0)
|
||||||
|
standard-custom (~> 1.0.0)
|
||||||
|
standard-performance (~> 1.1.0)
|
||||||
|
standard-custom (1.0.1)
|
||||||
|
lint_roller (~> 1.0)
|
||||||
|
standard-performance (1.1.0)
|
||||||
|
lint_roller (~> 1.0)
|
||||||
|
rubocop-performance (~> 1.18.0)
|
||||||
|
stringio (3.0.1)
|
||||||
|
test-unit (3.6.1)
|
||||||
|
power_assert
|
||||||
|
unicode-display_width (2.4.2)
|
||||||
|
|
||||||
|
PLATFORMS
|
||||||
|
x86_64-openbsd
|
||||||
|
|
||||||
|
DEPENDENCIES
|
||||||
|
rack-test (~> 2.1)
|
||||||
|
server.rb!
|
||||||
|
standard (~> 1.24)
|
||||||
|
test-unit (~> 3.5)
|
||||||
|
|
||||||
|
BUNDLED WITH
|
||||||
|
2.3.26
|
|
@ -1,15 +0,0 @@
|
||||||
Copyright (C) 2023 by 0x1eef <0x1eef@protonmail.com>
|
|
||||||
|
|
||||||
Permission to use, copy, modify, and/or distribute this
|
|
||||||
software for any purpose with or without fee is hereby
|
|
||||||
granted.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS
|
|
||||||
ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
|
|
||||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO
|
|
||||||
EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
||||||
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
|
|
||||||
RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
||||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
|
|
||||||
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
|
|
||||||
OF THIS SOFTWARE.
|
|
|
@ -1,40 +0,0 @@
|
||||||
## About
|
|
||||||
|
|
||||||
server.rb implements a static file web server
|
|
||||||
by using the fast performing Ruby web server
|
|
||||||
[Puma](https://github.com/puma/puma)
|
|
||||||
and a small
|
|
||||||
[Rack](https://github.com/rack/rack)
|
|
||||||
application.
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
### Server.for_dir
|
|
||||||
|
|
||||||
The `Server.for_dir` method returns a Server instance
|
|
||||||
that serves the contents of a directory. `Server#start` spawns
|
|
||||||
a new thread to listen for requests, and afterwards returns
|
|
||||||
the thread. `Thread#join` can block execution at that point,
|
|
||||||
or execution can continue as normal by not calling `Thread#join`:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
require "server"
|
|
||||||
|
|
||||||
##
|
|
||||||
# Create a Server instance for the contents of a directory
|
|
||||||
server = Server.for_dir("./build/website/")
|
|
||||||
|
|
||||||
##
|
|
||||||
# Start listening for connections
|
|
||||||
thr = server.start
|
|
||||||
|
|
||||||
##
|
|
||||||
# Prevent the main thread from exiting
|
|
||||||
thr.join
|
|
||||||
```
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
[BSD Zero Clause](https://choosealicense.com/licenses/0bsd/).
|
|
||||||
<br>
|
|
||||||
See [LICENSE](./LICENSE).
|
|
|
@ -1,52 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
class Server
|
|
||||||
require "rack"
|
|
||||||
require_relative "server/puma"
|
|
||||||
require_relative "server/gzip"
|
|
||||||
require_relative "server/etag"
|
|
||||||
require_relative "server/dir"
|
|
||||||
|
|
||||||
def self.app(path)
|
|
||||||
Rack::Builder.app do
|
|
||||||
use Server::ETag
|
|
||||||
run Server::Dir.new(path)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.for_dir(path, options = {})
|
|
||||||
host = options.delete(:host) || "127.0.0.1"
|
|
||||||
port = options.delete(:port) || 7777
|
|
||||||
unix = options.delete(:unix) || false
|
|
||||||
new app(path), options.merge!(
|
|
||||||
binds: [unix ? "unix://#{unix}" : "tcp://#{host}:#{port}"]
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def initialize(app, options = {})
|
|
||||||
@app = app
|
|
||||||
@options = default_options.merge!(options)
|
|
||||||
@events = Puma::Events.new
|
|
||||||
@server = Puma::Server.new(@app, @events, @options)
|
|
||||||
end
|
|
||||||
|
|
||||||
def start(block: false)
|
|
||||||
@server.binder.parse(@options[:binds])
|
|
||||||
thr = @server.run
|
|
||||||
block ? thr.join : thr
|
|
||||||
end
|
|
||||||
|
|
||||||
def stop
|
|
||||||
@server.stop
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def default_options
|
|
||||||
{
|
|
||||||
min_threads: 1,
|
|
||||||
max_threads: 5,
|
|
||||||
workers: 1
|
|
||||||
}
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,47 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
##
|
|
||||||
# A rack application that serves the contents
|
|
||||||
# of a directory over HTTP.
|
|
||||||
class Server::Dir
|
|
||||||
prepend Server::Gzip
|
|
||||||
|
|
||||||
def initialize(root)
|
|
||||||
@root = File.realpath(root)
|
|
||||||
@mime_types = {".ttf" => "font/ttf"}.freeze
|
|
||||||
end
|
|
||||||
|
|
||||||
def call(env)
|
|
||||||
finish Rack::Request.new(env)
|
|
||||||
rescue Errno::EPERM, Errno::EACCES
|
|
||||||
body = "Permission denied"
|
|
||||||
[403, {"content-length" => body.bytesize, "content-type" => "text/plain"}, [body]]
|
|
||||||
rescue Errno::ENOENT
|
|
||||||
body = "The requested URL was not found"
|
|
||||||
[404, {"content-length" => body.bytesize, "content-type" => "text/plain"}, [body]]
|
|
||||||
rescue => ex
|
|
||||||
body = "Internal server error (#{ex.class})"
|
|
||||||
[500, {"content-length" => body.bytesize, "content-type" => "text/plain"}, [body]]
|
|
||||||
end
|
|
||||||
|
|
||||||
def finish(request)
|
|
||||||
path = find_path(request)
|
|
||||||
body = File.binread(path)
|
|
||||||
extn = File.extname(path)
|
|
||||||
[
|
|
||||||
200,
|
|
||||||
{"content-type" => mime_types[extn] || Rack::Mime.mime_type(extn),
|
|
||||||
"content-length" => body.bytesize},
|
|
||||||
body.each_line.to_a
|
|
||||||
]
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
attr_reader :root, :mime_types
|
|
||||||
|
|
||||||
def find_path(request)
|
|
||||||
path = File.join root, File.expand_path(request.path)
|
|
||||||
File.directory?(path) ? File.join(path, "index.html") : path
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,18 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
class Server::ETag < Rack::ETag
|
|
||||||
ETAGS = {}
|
|
||||||
|
|
||||||
def initialize(app)
|
|
||||||
@app = app
|
|
||||||
end
|
|
||||||
|
|
||||||
def call(env)
|
|
||||||
status, headers, body = super(env)
|
|
||||||
if headers["etag"] && headers["etag"] == env["HTTP_IF_NONE_MATCH"]
|
|
||||||
[304, headers, [""]]
|
|
||||||
else
|
|
||||||
[status, headers, body]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,33 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
##
|
|
||||||
# A mixin module that serves a compressed version of
|
|
||||||
# a file when the file is found to exist on disk,
|
|
||||||
# and has a ".gz" file extension.
|
|
||||||
module Server::Gzip
|
|
||||||
def finish(request)
|
|
||||||
path = gzip_path(request)
|
|
||||||
if path
|
|
||||||
body = File.binread(path)
|
|
||||||
extn = File.extname(path[0..-4])
|
|
||||||
[
|
|
||||||
200,
|
|
||||||
{"content-type" => mime_types[extn] || Rack::Mime.mime_type(extn),
|
|
||||||
"content-encoding" => "gzip",
|
|
||||||
"content-length" => body.bytesize},
|
|
||||||
body.each_line
|
|
||||||
]
|
|
||||||
else
|
|
||||||
super
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def gzip_path(request)
|
|
||||||
return unless request.get_header("accept-encoding")
|
|
||||||
&.include?("gzip")
|
|
||||||
path = "#{find_path(request)}.gz"
|
|
||||||
File.exist?(path) ? path : nil
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,8 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
require "puma"
|
|
||||||
require "puma/events"
|
|
||||||
|
|
||||||
class Puma::Server
|
|
||||||
public :binder
|
|
||||||
end
|
|
|
@ -1,19 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
Gem::Specification.new do |gem|
|
|
||||||
gem.name = "server.rb"
|
|
||||||
gem.authors = ["0x1eef"]
|
|
||||||
gem.email = ["0x1eef@protonmail.com"]
|
|
||||||
gem.homepage = "https://github.com/0x1eef/server.rb#readme"
|
|
||||||
gem.version = "0.1.0"
|
|
||||||
gem.licenses = ["0BSD"]
|
|
||||||
gem.files = Dir["lib/*", "lib/**/*.rb"]
|
|
||||||
gem.require_paths = ["lib"]
|
|
||||||
gem.summary = "A static file web server"
|
|
||||||
gem.description = gem.summary
|
|
||||||
|
|
||||||
gem.add_runtime_dependency "puma", "~> 6.3"
|
|
||||||
gem.add_runtime_dependency "rack", "~> 3.0"
|
|
||||||
gem.add_development_dependency "standard", "~> 1.24"
|
|
||||||
gem.add_development_dependency "rack-test", "~> 2.1"
|
|
||||||
gem.add_development_dependency "test-unit", "~> 3.5"
|
|
||||||
end
|
|
|
@ -1,87 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
require_relative "setup"
|
|
||||||
require "rack/test"
|
|
||||||
|
|
||||||
class ServerDirTest < Test::Unit::TestCase
|
|
||||||
include Rack::Test::Methods
|
|
||||||
|
|
||||||
def test_index
|
|
||||||
get "/"
|
|
||||||
assert_equal 200, last_response.status
|
|
||||||
assert_equal "text/html", last_response.content_type
|
|
||||||
assert_equal bytesize("./test/webroot/index.html"), last_response.content_length
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_ttf_font
|
|
||||||
get "/fonts/roboto-mono-regular.ttf"
|
|
||||||
assert_equal 200, last_response.status
|
|
||||||
assert_equal "font/ttf", last_response.content_type
|
|
||||||
assert_equal bytesize("./test/webroot/fonts/roboto-mono-regular.ttf"),
|
|
||||||
last_response.content_length
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_js_file
|
|
||||||
get "/js/index.js"
|
|
||||||
assert_equal 200, last_response.status
|
|
||||||
assert_equal "application/javascript", last_response.content_type
|
|
||||||
assert_equal bytesize("./test/webroot/js/index.js"),
|
|
||||||
last_response.content_length
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_png_file
|
|
||||||
get "/images/0x1eef.png"
|
|
||||||
assert_equal 200, last_response.status
|
|
||||||
assert_equal "image/png", last_response.content_type
|
|
||||||
assert_equal bytesize("./test/webroot/images/0x1eef.png"),
|
|
||||||
last_response.content_length
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_json_file
|
|
||||||
get "/json/1.json"
|
|
||||||
assert_equal 200, last_response.status
|
|
||||||
assert_equal "application/json", last_response.content_type
|
|
||||||
assert_equal bytesize("./test/webroot/json/1.json"),
|
|
||||||
last_response.content_length
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_internal_server_error
|
|
||||||
def app.finish(request) raise "test" end
|
|
||||||
get "/"
|
|
||||||
assert_equal 500, last_response.status
|
|
||||||
assert_equal "text/plain", last_response.content_type
|
|
||||||
assert_equal "Internal server error (RuntimeError)".bytesize,
|
|
||||||
last_response.content_length
|
|
||||||
assert_equal "Internal server error (RuntimeError)",
|
|
||||||
last_response.body
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_permission_denied
|
|
||||||
File.chmod 0, "./test/webroot/permission_denied.html"
|
|
||||||
get "/permission_denied.html"
|
|
||||||
assert_equal 403, last_response.status
|
|
||||||
assert_equal "text/plain", last_response.content_type
|
|
||||||
assert_equal "Permission denied".bytesize, last_response.content_length
|
|
||||||
assert_equal "Permission denied", last_response.body
|
|
||||||
ensure
|
|
||||||
File.chmod 0o440, "./test/webroot/permission_denied.html"
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_page_not_found
|
|
||||||
get "/foobarbaz"
|
|
||||||
assert_equal 404, last_response.status
|
|
||||||
assert_equal "text/plain", last_response.content_type
|
|
||||||
assert_equal "The requested URL was not found".bytesize, last_response.content_length
|
|
||||||
assert_equal "The requested URL was not found", last_response.body
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def app
|
|
||||||
@app ||= Server.app("./test/webroot/")
|
|
||||||
end
|
|
||||||
|
|
||||||
def bytesize(path)
|
|
||||||
File.binread(path).bytesize
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,3 +0,0 @@
|
||||||
require "bundler/setup"
|
|
||||||
require "test/unit"
|
|
||||||
require "server"
|
|
Binary file not shown.
Binary file not shown.
Before Width: | Height: | Size: 7.3 KiB |
|
@ -1,8 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<head>
|
|
||||||
<title>/index.html</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<b>/index.html</b>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -1,3 +0,0 @@
|
||||||
(function() {
|
|
||||||
console.log("Hello world");
|
|
||||||
})();
|
|
|
@ -1 +0,0 @@
|
||||||
[{"id":"1","place_of_revelation":"makkah","transliterated_name":"Al-Fatihah","translated_name":"The Opener","verse_count":7,"slug":"al-fatihah","codepoints":[1575,1604,1601,1575,1578,1581,1577]},[1,"بِسْمِ اللَّهِ الرَّحْمَـٰنِ الرَّحِيمِ"],[2,"الْحَمْدُ لِلَّهِ رَبِّ الْعَالَمِينَ"],[3,"الرَّحْمَـٰنِ الرَّحِيمِ"],[4,"مَالِكِ يَوْمِ الدِّينِ"],[5,"إِيَّاكَ نَعْبُدُ وَإِيَّاكَ نَسْتَعِينُ"],[6,"اهْدِنَا الصِّرَاطَ الْمُسْتَقِيمَ"],[7,"صِرَاطَ الَّذِينَ أَنْعَمْتَ عَلَيْهِمْ غَيْرِ الْمَغْضُوبِ عَلَيْهِمْ وَلَا الضَّالِّينَ"]]
|
|
Loading…
Reference in a new issue