server.rb 0.2.2 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/server/index_resolver.rb +28 -0
- data/lib/server/version.rb +1 -1
- data/lib/server.rb +21 -19
- metadata +4 -6
- data/lib/server/dir.rb +0 -52
- data/lib/server/etag.rb +0 -31
- data/lib/server/gzip.rb +0 -33
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d6f5a23f88fe1b283e2b5004394f493a588060e653a28f72840ff5a194108d41
|
4
|
+
data.tar.gz: 479b0da5bf427fd73ef5ade931b713f342dc5be8d0e5409d12041b323e3c3c43
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 213694f2d265096664eaee7e7eab3f75f95fa361155e7c2671118dd02768f41bd9d1dab1d77eb320deb9c584c904207ee57025b7446bf8657bbde68f596c1620
|
7
|
+
data.tar.gz: c2182a37c1af04a2cac3e9826e3d70a145810dd9f2fb8d76a6a52a5b1ecdfcae2c9f4d87ee72bf39aa3284a3fb9c9d09ed47cf9f8b60b377b2daf7fa39b426de
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
##
|
4
|
+
# {Server::IndexResolver Server::IndexResolver} is a
|
5
|
+
# middleware that serves the contents of index.html
|
6
|
+
# from a given directory
|
7
|
+
class Server::IndexResolver
|
8
|
+
def initialize(app, options = {})
|
9
|
+
@app = app
|
10
|
+
@root = options.fetch(:root)
|
11
|
+
end
|
12
|
+
|
13
|
+
def call(env)
|
14
|
+
index = File.join(root, File.expand_path(env["PATH_INFO"]), "index.html")
|
15
|
+
if File.exist?(index)
|
16
|
+
Rack::Response[
|
17
|
+
200,
|
18
|
+
{"content-type" => "text/html"},
|
19
|
+
File.read(index).each_line
|
20
|
+
].finish
|
21
|
+
else
|
22
|
+
@app.call(env)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
attr_reader :root
|
28
|
+
end
|
data/lib/server/version.rb
CHANGED
data/lib/server.rb
CHANGED
@@ -3,34 +3,39 @@
|
|
3
3
|
class Server
|
4
4
|
require "rack"
|
5
5
|
require_relative "server/puma"
|
6
|
-
require_relative "server/
|
7
|
-
require_relative "server/etag"
|
8
|
-
require_relative "server/dir"
|
6
|
+
require_relative "server/index_resolver"
|
9
7
|
|
10
8
|
##
|
11
|
-
# @param [String]
|
12
|
-
# The path to a directory
|
13
|
-
#
|
9
|
+
# @param [String] root
|
10
|
+
# The path to a directory
|
14
11
|
# @return [Rack::Builder]
|
15
|
-
# Returns a Rack app
|
16
|
-
def self.app(
|
12
|
+
# Returns a Rack app
|
13
|
+
def self.app(root)
|
17
14
|
Rack::Builder.app do
|
18
|
-
use
|
19
|
-
|
15
|
+
use IndexResolver, {root:}
|
16
|
+
use Rack::Deflater
|
17
|
+
run Rack::Files.new(root)
|
20
18
|
end
|
21
19
|
end
|
22
|
-
private_class_method :app
|
23
20
|
|
24
21
|
##
|
25
22
|
# @param [String] path
|
26
23
|
# The path to a directory
|
27
|
-
#
|
28
24
|
# @param [Hash] options
|
29
25
|
# Hash of options
|
30
|
-
#
|
31
26
|
# @return [Server]
|
32
27
|
# Returns an instance of {Server Server}
|
33
28
|
def self.for(path, options = {})
|
29
|
+
new app(path), prepare(options)
|
30
|
+
end
|
31
|
+
|
32
|
+
##
|
33
|
+
# Prepares options for {Server#initialize Server#initialize}
|
34
|
+
# @param [#to_h] options
|
35
|
+
# @return [Hash]
|
36
|
+
# Returns a Hash object
|
37
|
+
def self.prepare(options)
|
38
|
+
options = options.to_h.dup
|
34
39
|
host = options.delete(:host) ||
|
35
40
|
options.delete("host") ||
|
36
41
|
options.delete(:bind) ||
|
@@ -42,10 +47,12 @@ class Server
|
|
42
47
|
unix = options.delete(:unix) ||
|
43
48
|
options.delete("unix") ||
|
44
49
|
nil
|
45
|
-
|
50
|
+
options.merge!(
|
46
51
|
binds: [unix ? "unix://#{unix}" : "tcp://#{host}:#{port}"]
|
47
52
|
)
|
53
|
+
options
|
48
54
|
end
|
55
|
+
|
49
56
|
class << self
|
50
57
|
alias_method :dir, :for
|
51
58
|
end
|
@@ -53,10 +60,8 @@ class Server
|
|
53
60
|
##
|
54
61
|
# @param [Rack::Builder] app
|
55
62
|
# Rack application
|
56
|
-
#
|
57
63
|
# @param [Hash] options
|
58
64
|
# Hash of options
|
59
|
-
#
|
60
65
|
# @return [Server]
|
61
66
|
# Returns an instance of {Server Server}
|
62
67
|
def initialize(app, options = {})
|
@@ -68,10 +73,8 @@ class Server
|
|
68
73
|
|
69
74
|
##
|
70
75
|
# Starts the web server
|
71
|
-
#
|
72
76
|
# @param [Boolean] block
|
73
77
|
# When given as true, this method will block
|
74
|
-
#
|
75
78
|
# @return [Thread]
|
76
79
|
# Returns a thread
|
77
80
|
def start(block: false)
|
@@ -82,7 +85,6 @@ class Server
|
|
82
85
|
|
83
86
|
##
|
84
87
|
# Stops the web server
|
85
|
-
#
|
86
88
|
# @return [void]
|
87
89
|
def stop
|
88
90
|
@server.stop
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: server.rb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- '0x1eef'
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-08-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: puma
|
@@ -88,9 +88,7 @@ extensions: []
|
|
88
88
|
extra_rdoc_files: []
|
89
89
|
files:
|
90
90
|
- lib/server.rb
|
91
|
-
- lib/server/
|
92
|
-
- lib/server/etag.rb
|
93
|
-
- lib/server/gzip.rb
|
91
|
+
- lib/server/index_resolver.rb
|
94
92
|
- lib/server/puma.rb
|
95
93
|
- lib/server/version.rb
|
96
94
|
homepage: https://github.com/0x1eef/server.rb#readme
|
@@ -112,7 +110,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
112
110
|
- !ruby/object:Gem::Version
|
113
111
|
version: '0'
|
114
112
|
requirements: []
|
115
|
-
rubygems_version: 3.5.
|
113
|
+
rubygems_version: 3.5.13
|
116
114
|
signing_key:
|
117
115
|
specification_version: 4
|
118
116
|
summary: A static file web server
|
data/lib/server/dir.rb
DELETED
@@ -1,52 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
##
|
4
|
-
# A rack application that serves the contents
|
5
|
-
# of a directory over HTTP.
|
6
|
-
class Server::Dir
|
7
|
-
prepend Server::Gzip
|
8
|
-
|
9
|
-
def initialize(root)
|
10
|
-
@root = File.realpath(root)
|
11
|
-
@mime_types = {".ttf" => "font/ttf"}.freeze
|
12
|
-
end
|
13
|
-
|
14
|
-
def call(env)
|
15
|
-
catch(:redirect) { finish Rack::Request.new(env) }
|
16
|
-
rescue Errno::EPERM, Errno::EACCES
|
17
|
-
body = "Permission denied"
|
18
|
-
[403, {"content-length" => body.bytesize, "content-type" => "text/plain"}, [body]]
|
19
|
-
rescue Errno::ENOENT
|
20
|
-
body = "The requested URL was not found"
|
21
|
-
[404, {"content-length" => body.bytesize, "content-type" => "text/plain"}, [body]]
|
22
|
-
rescue => ex
|
23
|
-
body = "Internal server error (#{ex.class})"
|
24
|
-
[500, {"content-length" => body.bytesize, "content-type" => "text/plain"}, [body]]
|
25
|
-
end
|
26
|
-
|
27
|
-
def finish(req)
|
28
|
-
path = resolve_path(req)
|
29
|
-
body = File.binread(path)
|
30
|
-
extn = File.extname(path)
|
31
|
-
[
|
32
|
-
200,
|
33
|
-
{"content-type" => mime_types[extn] || Rack::Mime.mime_type(extn),
|
34
|
-
"content-length" => body.bytesize},
|
35
|
-
body.each_line.to_a
|
36
|
-
]
|
37
|
-
end
|
38
|
-
|
39
|
-
private
|
40
|
-
|
41
|
-
attr_reader :root, :mime_types
|
42
|
-
|
43
|
-
def resolve_path(req)
|
44
|
-
path = File.join root, File.expand_path(req.path)
|
45
|
-
return path unless File.directory?(path)
|
46
|
-
if req.path.end_with?("/")
|
47
|
-
File.join(path, "index.html")
|
48
|
-
else
|
49
|
-
throw(:redirect, [301, {"Location" => "#{req.path}/"}, [""]])
|
50
|
-
end
|
51
|
-
end
|
52
|
-
end
|
data/lib/server/etag.rb
DELETED
@@ -1,31 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
##
|
4
|
-
# A middleware that adds ETag support.
|
5
|
-
class Server::ETag < Rack::ETag
|
6
|
-
ETAGS = {}
|
7
|
-
|
8
|
-
##
|
9
|
-
# @param [#call] app
|
10
|
-
# A rack app.
|
11
|
-
#
|
12
|
-
# @return [Server::ETag]
|
13
|
-
# Reurns an instance of {Server::ETag Server::ETag}.
|
14
|
-
def initialize(app)
|
15
|
-
@app = app
|
16
|
-
end
|
17
|
-
|
18
|
-
##
|
19
|
-
# @param [Hash] env
|
20
|
-
# An environment hash.
|
21
|
-
#
|
22
|
-
# @return [Array<Number, Hash, #each>]
|
23
|
-
def call(env)
|
24
|
-
status, headers, body = super(env)
|
25
|
-
if headers["etag"] && headers["etag"] == env["HTTP_IF_NONE_MATCH"]
|
26
|
-
[304, headers, [""]]
|
27
|
-
else
|
28
|
-
[status, headers, body]
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
data/lib/server/gzip.rb
DELETED
@@ -1,33 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
##
|
4
|
-
# A mixin that serves a compressed copy of a file.
|
5
|
-
# Similar to the nginx module
|
6
|
-
# [gzip_static](http://nginx.org/en/docs/http/ngx_http_gzip_static_module.html).
|
7
|
-
module Server::Gzip
|
8
|
-
def finish(request)
|
9
|
-
path = gzip_path(request)
|
10
|
-
if path
|
11
|
-
body = File.binread(path)
|
12
|
-
extn = File.extname(path[0..-4])
|
13
|
-
[
|
14
|
-
200,
|
15
|
-
{"content-type" => mime_types[extn] || Rack::Mime.mime_type(extn),
|
16
|
-
"content-encoding" => "gzip",
|
17
|
-
"content-length" => body.bytesize},
|
18
|
-
body.each_line
|
19
|
-
]
|
20
|
-
else
|
21
|
-
super
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
private
|
26
|
-
|
27
|
-
def gzip_path(request)
|
28
|
-
return unless request.get_header("accept-encoding")
|
29
|
-
&.include?("gzip")
|
30
|
-
path = "#{find_path(request)}.gz"
|
31
|
-
File.exist?(path) ? path : nil
|
32
|
-
end
|
33
|
-
end
|