spyder 0.2.0 → 0.3.0.pre.beta1
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/lib/spyder/basic_server.rb +29 -0
- data/lib/spyder/rack_adapter.rb +38 -0
- data/lib/spyder/request.rb +11 -1
- data/lib/spyder/router.rb +8 -0
- data/lib/spyder/server.rb +65 -53
- data/lib/spyder/version.rb +1 -1
- data/lib/spyder/web/file_server.rb +6 -2
- data/lib/spyder.rb +1 -0
- metadata +19 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 2e1ade7f9c9ee1b99448fc6a0fb8addcbbca80a2696581b1bb4fa05c27ea534a
|
|
4
|
+
data.tar.gz: 075d91b41bdd5a7264bfadd3e014ef67b7c311545417b1503c9a245935f12860
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f0cc009cdbf9d1ab3bf184be0214c6d1e0366b6d0823dc6df8c2b548db4e2b631d972f8e7a2dd2ec9fabfdcb2b6135e4ba1c79d3276d6bc5864005df73f32750
|
|
7
|
+
data.tar.gz: ba21a6413f6cf9ffe290806cd7ee7b96c877b592243d3549caaa374dd455a40037e2f240f41743c83fcc753f0cf97ad682892f2c0cfe4f96d3f99ad50f77bf5c
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Spyder
|
|
4
|
+
class BasicServer
|
|
5
|
+
attr_accessor :router
|
|
6
|
+
|
|
7
|
+
def initialize(router)
|
|
8
|
+
@router = router
|
|
9
|
+
@middleware = []
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def process_request(request)
|
|
13
|
+
mids = @middleware + [[RouterApp, @router]]
|
|
14
|
+
app = nil
|
|
15
|
+
|
|
16
|
+
loop do
|
|
17
|
+
klass, args = mids.pop
|
|
18
|
+
break unless klass
|
|
19
|
+
app = klass.new(args, app)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
app.call({}, request)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def add_middleware(callable, args=[])
|
|
26
|
+
@middleware << [callable, args]
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
require 'spyder'
|
|
3
|
+
|
|
4
|
+
module Spyder
|
|
5
|
+
class RackServer < BasicServer
|
|
6
|
+
attr_accessor :router
|
|
7
|
+
|
|
8
|
+
def initialize(router: Router.new)
|
|
9
|
+
super(router)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def call(env)
|
|
13
|
+
qs = env[Rack::QUERY_STRING]
|
|
14
|
+
|
|
15
|
+
request = Request.new
|
|
16
|
+
request.path = (!qs || qs.empty? ? env[Rack::PATH_INFO] : "#{env[Rack::PATH_INFO]}?#{qs}")
|
|
17
|
+
request.verb = env[Rack::REQUEST_METHOD]
|
|
18
|
+
request.http_version = env['HTTP_VERSION']
|
|
19
|
+
request.io = env['rack.input']
|
|
20
|
+
|
|
21
|
+
env.each do |key, value|
|
|
22
|
+
next unless key.start_with?('HTTP_') && key != 'HTTP_VERSION'
|
|
23
|
+
|
|
24
|
+
header_name = key[5..].downcase.tr('_', '-')
|
|
25
|
+
request.add_header(header_name, value)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
response = process_request(request)
|
|
29
|
+
|
|
30
|
+
[
|
|
31
|
+
response.code,
|
|
32
|
+
# response.headers.ordered.map { |k, v| ["HTTP_#{k.tr('-', '_').upcase}", v] },
|
|
33
|
+
response.headers.ordered,
|
|
34
|
+
[response.body]
|
|
35
|
+
]
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
data/lib/spyder/request.rb
CHANGED
|
@@ -7,7 +7,7 @@ module Spyder
|
|
|
7
7
|
attr_accessor :verb
|
|
8
8
|
attr_accessor :path
|
|
9
9
|
attr_accessor :headers
|
|
10
|
-
attr_accessor :
|
|
10
|
+
attr_accessor :http_version, :remote_address
|
|
11
11
|
attr_accessor :io
|
|
12
12
|
|
|
13
13
|
def_delegators :@headers, :ordered, :dict, :add_header
|
|
@@ -73,6 +73,16 @@ module Spyder
|
|
|
73
73
|
len ? io.read(Integer(len)) : io.read # FIXME:
|
|
74
74
|
end
|
|
75
75
|
|
|
76
|
+
def debug_dump(include_body: false)
|
|
77
|
+
lines = ["verb=#{@verb}\npath=#{@path}\nhttp_version=#{@http_version}"]
|
|
78
|
+
lines << "has #{@headers.dict.length} headers"
|
|
79
|
+
@headers.dict.each do |name, value|
|
|
80
|
+
lines << "\t#{name} = #{value}"
|
|
81
|
+
end
|
|
82
|
+
lines << "has body: #{has_body? ? 'yes' : 'no'}"
|
|
83
|
+
lines.join("\n")
|
|
84
|
+
end
|
|
85
|
+
|
|
76
86
|
def has_body?
|
|
77
87
|
dict['transfer-encoding'] != nil || dict['content-length'] != nil
|
|
78
88
|
end
|
data/lib/spyder/router.rb
CHANGED
|
@@ -9,6 +9,14 @@ module Spyder
|
|
|
9
9
|
@fallback_stack = []
|
|
10
10
|
end
|
|
11
11
|
|
|
12
|
+
def add_route_method(verb, matcher, obj, method_name)
|
|
13
|
+
h = proc do |req, rq|
|
|
14
|
+
obj.public_send(method_name, req, rq)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
add_route(verb, matcher, &h)
|
|
18
|
+
end
|
|
19
|
+
|
|
12
20
|
def add_route(verb, matcher, &handler)
|
|
13
21
|
matcher = Mustermann.new(matcher) if matcher.is_a?(String)
|
|
14
22
|
|
data/lib/spyder/server.rb
CHANGED
|
@@ -1,88 +1,100 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Spyder
|
|
4
|
-
class Server
|
|
4
|
+
class Server < BasicServer
|
|
5
5
|
attr_accessor :router
|
|
6
6
|
|
|
7
7
|
def initialize(bind, port, router: Router.new, max_threads: 4, tcp_backlog: 10)
|
|
8
|
+
super(router)
|
|
9
|
+
|
|
8
10
|
@server = TCPServer.new(bind, port)
|
|
9
11
|
@tcp_backlog = tcp_backlog
|
|
10
12
|
@max_threads = max_threads
|
|
11
|
-
@
|
|
12
|
-
@
|
|
13
|
+
@max_queue_size = max_threads * tcp_backlog
|
|
14
|
+
@tp_read, @tp_write = IO.pipe
|
|
13
15
|
@tp_sync = Mutex.new
|
|
14
|
-
@
|
|
16
|
+
@waiting_clients = []
|
|
17
|
+
@threads = max_threads.times.map { create_request_handler_thread }
|
|
15
18
|
end
|
|
16
19
|
|
|
17
|
-
def
|
|
18
|
-
|
|
20
|
+
def create_request_handler_thread
|
|
21
|
+
Thread.new do
|
|
22
|
+
loop do
|
|
23
|
+
@tp_read.read(1)
|
|
24
|
+
client = nil
|
|
25
|
+
|
|
26
|
+
@tp_sync.synchronize do
|
|
27
|
+
client = @waiting_clients.shift
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
handle_new_client(client)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def handle_new_client(client)
|
|
36
|
+
error, response = nil
|
|
37
|
+
|
|
38
|
+
request = read_client_request(client)
|
|
39
|
+
|
|
40
|
+
begin
|
|
41
|
+
response = process_request(request)
|
|
42
|
+
rescue Exception => e
|
|
43
|
+
error = e
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
if error
|
|
47
|
+
puts "Request errored in spyder. Error dump:"
|
|
48
|
+
puts error.full_message
|
|
49
|
+
puts "Request dump:\n#{request.debug_dump}"
|
|
50
|
+
|
|
51
|
+
response = Response.make_generic :internal_server_error
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
dispatch_response(client, response)
|
|
55
|
+
|
|
56
|
+
if !error && response&.hijack
|
|
57
|
+
response.hijack.call(client)
|
|
58
|
+
else
|
|
59
|
+
client.close rescue nil
|
|
60
|
+
end
|
|
19
61
|
end
|
|
20
62
|
|
|
21
63
|
def start
|
|
22
|
-
busy_threads = 0
|
|
23
64
|
@server.listen(@tcp_backlog)
|
|
24
65
|
|
|
25
66
|
loop do
|
|
26
67
|
time_start = Process.clock_gettime(:CLOCK_MONOTONIC, :float_second)
|
|
27
68
|
loop do
|
|
28
|
-
|
|
29
|
-
|
|
69
|
+
queue_size = nil
|
|
70
|
+
@tp_sync.synchronize do
|
|
71
|
+
queue_size = @waiting_clients.length
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
break if queue_size < @max_queue_size # current_busy < @max_threads
|
|
30
75
|
sleep(0)
|
|
31
76
|
current_time = Process.clock_gettime(:CLOCK_MONOTONIC, :float_second)
|
|
32
77
|
if (current_time - time_start) > 1.0
|
|
33
|
-
|
|
78
|
+
puts "Waiting a long time: #{(current_time - time_start)}"
|
|
34
79
|
sleep 0.2
|
|
35
80
|
end
|
|
36
81
|
end
|
|
37
82
|
|
|
38
|
-
|
|
39
|
-
@tp_sync.synchronize
|
|
40
|
-
|
|
41
|
-
Thread.new do
|
|
42
|
-
begin
|
|
43
|
-
error, response = nil
|
|
44
|
-
begin
|
|
45
|
-
response = process_new_client(client)
|
|
46
|
-
rescue Exception => e
|
|
47
|
-
error = e
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
if error
|
|
51
|
-
puts error.full_message
|
|
52
|
-
|
|
53
|
-
response = Response.make_generic :internal_server_error
|
|
54
|
-
dispatch_response(client, response)
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
if response&.hijack
|
|
58
|
-
response.hijack.call(client)
|
|
59
|
-
else
|
|
60
|
-
client.close rescue nil
|
|
61
|
-
end
|
|
62
|
-
ensure
|
|
63
|
-
@tp_sync.synchronize { busy_threads -= 1 }
|
|
64
|
-
end
|
|
83
|
+
client_socket = @server.accept
|
|
84
|
+
@tp_sync.synchronize do
|
|
85
|
+
@waiting_clients << client_socket
|
|
65
86
|
end
|
|
66
|
-
end
|
|
67
|
-
end
|
|
68
87
|
|
|
69
|
-
|
|
70
|
-
mids = @middleware + [[RouterApp, @router]]
|
|
71
|
-
app = nil
|
|
72
|
-
loop do
|
|
73
|
-
klass, args = mids.pop
|
|
74
|
-
break unless klass
|
|
75
|
-
app = klass.new(args, app)
|
|
88
|
+
@tp_write.write(1)
|
|
76
89
|
end
|
|
77
|
-
|
|
78
|
-
app.call({}, request)
|
|
79
90
|
end
|
|
80
91
|
|
|
81
|
-
def
|
|
82
|
-
verb, path,
|
|
92
|
+
def read_client_request(socket)
|
|
93
|
+
verb, path, http_version = read_line(socket).split(' ', 3)
|
|
83
94
|
request = Request.new
|
|
84
95
|
request.path = path
|
|
85
96
|
request.verb = verb
|
|
97
|
+
request.http_version = http_version
|
|
86
98
|
request.io = socket
|
|
87
99
|
|
|
88
100
|
loop do
|
|
@@ -94,9 +106,7 @@ module Spyder
|
|
|
94
106
|
request.add_header(name, value)
|
|
95
107
|
end
|
|
96
108
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
dispatch_response(socket, response)
|
|
109
|
+
request
|
|
100
110
|
end
|
|
101
111
|
|
|
102
112
|
def dispatch_response(socket, response)
|
|
@@ -104,6 +114,8 @@ module Spyder
|
|
|
104
114
|
if !content_length && response.body && response.body.is_a?(String)
|
|
105
115
|
content_length = response.body.length
|
|
106
116
|
response.set_header 'content-length', content_length.to_s
|
|
117
|
+
elsif !content_length && (200..203) === response.code
|
|
118
|
+
response.set_header 'content-length', '0'
|
|
107
119
|
end
|
|
108
120
|
|
|
109
121
|
response.set_header('connection', 'close') unless response.headers.dict['connection']
|
data/lib/spyder/version.rb
CHANGED
|
@@ -9,14 +9,18 @@ module Spyder
|
|
|
9
9
|
def initialize(paths, index: nil)
|
|
10
10
|
@default_index = index
|
|
11
11
|
@base_paths = Array(paths).map do |path|
|
|
12
|
-
|
|
12
|
+
if path[0] == '/'
|
|
13
|
+
path
|
|
14
|
+
else
|
|
15
|
+
File.expand_path(File.join(Dir.pwd, path))
|
|
16
|
+
end
|
|
13
17
|
end
|
|
14
18
|
end
|
|
15
19
|
|
|
16
20
|
def call(request)
|
|
17
21
|
return unless request.verb == 'GET'
|
|
18
22
|
|
|
19
|
-
input_path = request.
|
|
23
|
+
input_path = request.path_info
|
|
20
24
|
input_path = @default_index if request.path == '/' && @default_index
|
|
21
25
|
|
|
22
26
|
req_path = safe_request_path(input_path)
|
data/lib/spyder.rb
CHANGED
metadata
CHANGED
|
@@ -1,15 +1,28 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: spyder
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.0.pre.beta1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- André D. Piske
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: bin
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: base64
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - "~>"
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '0.3'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - "~>"
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '0.3'
|
|
13
26
|
- !ruby/object:Gem::Dependency
|
|
14
27
|
name: mustermann
|
|
15
28
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -46,8 +59,10 @@ extra_rdoc_files: []
|
|
|
46
59
|
files:
|
|
47
60
|
- lib/examples/simplest_server/server.rb
|
|
48
61
|
- lib/spyder.rb
|
|
62
|
+
- lib/spyder/basic_server.rb
|
|
49
63
|
- lib/spyder/error.rb
|
|
50
64
|
- lib/spyder/header_store.rb
|
|
65
|
+
- lib/spyder/rack_adapter.rb
|
|
51
66
|
- lib/spyder/request.rb
|
|
52
67
|
- lib/spyder/response.rb
|
|
53
68
|
- lib/spyder/router.rb
|
|
@@ -60,7 +75,6 @@ homepage: https://github.com/andrepiske/spyder
|
|
|
60
75
|
licenses:
|
|
61
76
|
- MIT
|
|
62
77
|
metadata: {}
|
|
63
|
-
post_install_message:
|
|
64
78
|
rdoc_options: []
|
|
65
79
|
require_paths:
|
|
66
80
|
- lib
|
|
@@ -75,8 +89,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
75
89
|
- !ruby/object:Gem::Version
|
|
76
90
|
version: '0'
|
|
77
91
|
requirements: []
|
|
78
|
-
rubygems_version:
|
|
79
|
-
signing_key:
|
|
92
|
+
rubygems_version: 4.0.23
|
|
80
93
|
specification_version: 4
|
|
81
94
|
summary: Spyder
|
|
82
95
|
test_files: []
|