intranet-core 2.0.0 → 2.2.0
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 +5 -5
- data/lib/core_extensions.rb +2 -0
- data/lib/core_extensions/hash.rb +15 -0
- data/lib/core_extensions/string.rb +0 -1
- data/lib/intranet/abstract_responder.rb +9 -0
- data/lib/intranet/core.rb +3 -1
- data/lib/intranet/core/builder.rb +13 -3
- data/lib/intranet/core/servlet.rb +10 -3
- data/lib/intranet/core/version.rb +1 -1
- data/lib/intranet/resources/www/nav.js +2 -1
- data/lib/intranet/resources/www/style.css +0 -1
- data/spec/core_extensions/hash_spec.rb +22 -0
- data/spec/intranet/core_spec.rb +244 -254
- data/spec/test_responder/responder.rb +11 -3
- metadata +7 -6
- data/spec/test_responder/www/style.css +0 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: a6860141eca0145a6d51787a137641ead4978b69d2f7b2d28ce5dcf1a5193f42
|
4
|
+
data.tar.gz: 6f43415cdf7334c81466b1205a838b92762c15dff3fb8ca3e096ccb71ebecfa2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 84eeb50385f879656140e5b385a98a50c3149fa7b6cb8d31fd49b97ce73d5f69eda2e3e33789876196c0eaccaa47721b089e7870a0b90880392f03aedb279600
|
7
|
+
data.tar.gz: 07e5122a4279dc99efe881651637e932ec36601b3007529e545b13b7ccaef2297ca05b7f6184f02a6693d61ad1147b162961c508992ce57ce09ce56a0a8de32e
|
data/lib/core_extensions.rb
CHANGED
@@ -1,9 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative 'core_extensions/hash'
|
3
4
|
require_relative 'core_extensions/string'
|
4
5
|
require_relative 'core_extensions/tree'
|
5
6
|
require_relative 'core_extensions/webrick/httpresponse'
|
6
7
|
|
8
|
+
Hash.include CoreExtensions::Hash
|
7
9
|
String.include CoreExtensions::String
|
8
10
|
WEBrick::HTTPResponse.include CoreExtensions::WEBrick::HTTPResponse
|
9
11
|
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CoreExtensions
|
4
|
+
# Extension of Ruby's standard library +Hash+ class.
|
5
|
+
module Hash
|
6
|
+
# Returns a copy of a hash with +keys+ excluded. Original hash is not
|
7
|
+
# modified.
|
8
|
+
# @return [Hash]
|
9
|
+
def except(*keys)
|
10
|
+
h = dup
|
11
|
+
keys.each { |key| h.delete(key) }
|
12
|
+
h
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -22,6 +22,15 @@ module Intranet
|
|
22
22
|
true
|
23
23
|
end
|
24
24
|
|
25
|
+
# Specifies the absolute path to the resources directory for that module. This directory should
|
26
|
+
# contain three subdirectories: +haml/+, +locales/+ and +www/+.
|
27
|
+
# This method should be redefined to overwrite this default value with the actual resources
|
28
|
+
# directory path.
|
29
|
+
# @return [String] The absolute path to the resources directory for the module.
|
30
|
+
def resources_dir
|
31
|
+
File.join(__dir__, 'resources')
|
32
|
+
end
|
33
|
+
|
25
34
|
# Destroys the responder instance.
|
26
35
|
# This method gets called when server is shut down.
|
27
36
|
def finalize
|
data/lib/intranet/core.rb
CHANGED
@@ -52,7 +52,7 @@ module Intranet
|
|
52
52
|
# mounted under +<path>/design+ on the web server.
|
53
53
|
# @raise [ArgumentError] If the +path+ is not valid.
|
54
54
|
# @raise [Errno::EALREADY] If the server is already running.
|
55
|
-
def register_module(responder, path, resources_dir)
|
55
|
+
def register_module(responder, path, resources_dir = responder.resources_dir)
|
56
56
|
raise Errno::EALREADY if @server.status != :Stop
|
57
57
|
|
58
58
|
@builder.register(responder, path)
|
@@ -80,6 +80,8 @@ module Intranet
|
|
80
80
|
def stop
|
81
81
|
@logger.info('Intranet::Runner: requesting system shutdown...')
|
82
82
|
@server.shutdown
|
83
|
+
while @server.status != :Stop
|
84
|
+
end
|
83
85
|
@builder.finalize
|
84
86
|
@logger.close
|
85
87
|
end
|
@@ -57,12 +57,12 @@ module Intranet
|
|
57
57
|
|
58
58
|
# Generate header and footer when partial content is returned by the responder
|
59
59
|
if status == 206 && mime_type == 'text/html'
|
60
|
-
body =
|
61
|
-
js: responder.js_dependencies, current_path: parsed_path)
|
60
|
+
body = add_header_and_footer(body, responder, parsed_path)
|
62
61
|
status = 200
|
63
62
|
end
|
64
63
|
[status, mime_type, body]
|
65
|
-
rescue KeyError, NoMethodError
|
64
|
+
rescue KeyError, NoMethodError => e
|
65
|
+
@logger.debug(e)
|
66
66
|
[404, '', '']
|
67
67
|
end
|
68
68
|
|
@@ -111,6 +111,16 @@ module Intranet
|
|
111
111
|
[current_treenode.value, '/' + parsed_path.join('/'), '/' + unparsed_path.join('/')]
|
112
112
|
end
|
113
113
|
|
114
|
+
# Encapsulate the given body in the default page skeleton, effectively
|
115
|
+
# adding page header and footer.
|
116
|
+
# @param body [String] The body of the page
|
117
|
+
# @param responder [Intranet::AbstractResponder] The responder that produced the body
|
118
|
+
# @param path [String] The path to the responder
|
119
|
+
def add_header_and_footer(body, responder, path)
|
120
|
+
to_markup('skeleton', body: body, css: responder.css_dependencies,
|
121
|
+
js: responder.js_dependencies, current_path: path)
|
122
|
+
end
|
123
|
+
|
114
124
|
# Check whether a path is valid.
|
115
125
|
# @param path [Array] The path to test.
|
116
126
|
# @return [Boolean] True if the path is valid, False otherwise.
|
@@ -26,12 +26,12 @@ module Intranet
|
|
26
26
|
# We could use request.request_uri (which is a URI object) but its path is not normalized
|
27
27
|
# (it may contain '../' or event start with '..'). Hence we use request.path which has been
|
28
28
|
# normalized with HTTPUtils::normalize_path, and request.query for the URL parameters.
|
29
|
-
path = request.path
|
29
|
+
path = request.path.force_encoding('UTF-8')
|
30
30
|
path += 'index.html' if path.end_with?('/')
|
31
31
|
|
32
32
|
handle_redirections(request, path, response, '/index.html' => @builder.home_url)
|
33
33
|
|
34
|
-
status, content_type, body = @builder.do_get(path, request.query)
|
34
|
+
status, content_type, body = @builder.do_get(path, encode_query(request.query))
|
35
35
|
|
36
36
|
raise WEBrick::HTTPStatus[status] if WEBrick::HTTPStatus.error?(status)
|
37
37
|
|
@@ -48,9 +48,16 @@ module Intranet
|
|
48
48
|
target = redirects.fetch(path)
|
49
49
|
location = URI.join(request.request_uri, target).to_s
|
50
50
|
response.set_redirect(WEBrick::HTTPStatus[307], location) if path != target
|
51
|
-
rescue KeyError
|
51
|
+
rescue KeyError
|
52
52
|
# nothing to do
|
53
53
|
end
|
54
|
+
|
55
|
+
# Reencodes the +query+ in UTF-8 strings for compatibility.
|
56
|
+
def encode_query(query)
|
57
|
+
query.map do |k, v|
|
58
|
+
{ k.dup.force_encoding('UTF-8') => v.force_encoding('UTF-8') }
|
59
|
+
end.reduce({}, :merge)
|
60
|
+
end
|
54
61
|
end
|
55
62
|
end
|
56
63
|
end
|
@@ -10,7 +10,8 @@ function openNavMenu() {
|
|
10
10
|
}
|
11
11
|
|
12
12
|
function closeNavMenu() {
|
13
|
-
|
13
|
+
/* Remove value property set in openNavMenu() */
|
14
|
+
document.querySelectorAll('header nav')[0].style.width = '';
|
14
15
|
}
|
15
16
|
|
16
17
|
function openModal() {
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'core_extensions/hash'
|
4
|
+
|
5
|
+
RSpec.describe CoreExtensions::Hash do
|
6
|
+
before do
|
7
|
+
Hash.include described_class
|
8
|
+
end
|
9
|
+
|
10
|
+
describe '#except' do
|
11
|
+
it 'should return a copy of the original hash with the given keys removed' do
|
12
|
+
tested_hash = { a: true, b: false, c: nil }
|
13
|
+
expect(tested_hash.except(:c)).to eql({ a: true, b: false })
|
14
|
+
expect(tested_hash).to eql({ a: true, b: false, c: nil })
|
15
|
+
|
16
|
+
tested_hash = { a: 100, b: 200, c: 300 }
|
17
|
+
expect(tested_hash.except(:a)).to eql({ b: 200, c: 300 })
|
18
|
+
expect(tested_hash.except(:a, :c)).to eql({ b: 200 })
|
19
|
+
expect(tested_hash).to eql({ a: 100, b: 200, c: 300 })
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
data/spec/intranet/core_spec.rb
CHANGED
@@ -15,71 +15,67 @@ RSpec.describe Intranet::Core do
|
|
15
15
|
|
16
16
|
describe '#start' do
|
17
17
|
it 'should start an HTTP server on an available port' do
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
# Wait for the server on port 8080 to be running
|
25
|
-
while @intranet8080.instance_variable_get(:@server).status != :Running
|
26
|
-
end
|
18
|
+
# make sure port 80 & 8080 are not available
|
19
|
+
@intranet8080 = described_class.new(Intranet::Logger.new(Intranet::Logger::FATAL))
|
20
|
+
thread8080 = Thread.new { @intranet8080.start }
|
21
|
+
while @intranet8080.instance_variable_get(:@server).status != :Running
|
22
|
+
end
|
23
|
+
expect(@intranet8080.port).to be >= 8080
|
27
24
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
)
|
32
|
-
expect(intranet.port).not_to eql(8080)
|
33
|
-
ensure
|
34
|
-
intranet.stop if intranet.respond_to?(:stop)
|
35
|
-
Thread.kill(thread)
|
36
|
-
thread.join
|
25
|
+
@intranet = described_class.new(Intranet::Logger.new(Intranet::Logger::FATAL))
|
26
|
+
thread = Thread.new { @intranet.start }
|
27
|
+
while @intranet.instance_variable_get(:@server).status != :Running
|
37
28
|
end
|
29
|
+
expect(@intranet.port).to be > 8080
|
30
|
+
|
31
|
+
@intranet.stop
|
32
|
+
thread&.join
|
33
|
+
@intranet8080.stop
|
34
|
+
thread8080&.join
|
38
35
|
end
|
39
36
|
|
40
37
|
it 'should start searching for an available port at the given +preferred_port+' do
|
41
|
-
intranet = described_class.new(Intranet::Logger.new(Intranet::Logger::FATAL), 9090)
|
42
|
-
|
38
|
+
@intranet = described_class.new(Intranet::Logger.new(Intranet::Logger::FATAL), 9090)
|
39
|
+
thread = Thread.new { @intranet.start }
|
40
|
+
while @intranet.instance_variable_get(:@server).status != :Running
|
41
|
+
end
|
42
|
+
|
43
|
+
expect(@intranet.port).to be >= 9090
|
44
|
+
|
45
|
+
@intranet.stop
|
46
|
+
thread&.join
|
43
47
|
end
|
44
48
|
|
45
49
|
it 'should serve the /design directory' do
|
46
|
-
|
50
|
+
@intranet = described_class.new(Intranet::Logger.new(Intranet::Logger::FATAL))
|
51
|
+
thread = Thread.new { @intranet.start }
|
52
|
+
while @intranet.instance_variable_get(:@server).status != :Running
|
53
|
+
end
|
54
|
+
|
55
|
+
socket = TCPSocket.new('localhost', @intranet.port)
|
56
|
+
socket.puts("GET /design/favicon.ico HTTP/1.1\r\nHost: localhost:#{@intranet.port}\r\n\r\n")
|
57
|
+
expect(socket.gets).to include('HTTP/1.1 200 OK')
|
58
|
+
socket.close
|
59
|
+
|
60
|
+
@intranet.stop
|
61
|
+
thread&.join
|
62
|
+
end
|
63
|
+
|
64
|
+
context 'when no module is registered' do
|
65
|
+
it 'should return HTTP error 404 when requested for /index.html' do
|
47
66
|
@intranet = described_class.new(Intranet::Logger.new(Intranet::Logger::FATAL))
|
48
|
-
thread = Thread.new
|
49
|
-
@intranet.start
|
50
|
-
end
|
67
|
+
thread = Thread.new { @intranet.start }
|
51
68
|
while @intranet.instance_variable_get(:@server).status != :Running
|
52
69
|
end
|
53
70
|
|
54
71
|
socket = TCPSocket.new('localhost', @intranet.port)
|
55
|
-
socket.puts("GET /
|
56
|
-
|
57
|
-
|
72
|
+
socket.puts("GET /index.html HTTP/1.1\r\n" \
|
73
|
+
"Host: localhost:#{@intranet.port}\r\n\r\n")
|
74
|
+
expect(socket.gets).to include('HTTP/1.1 404 Not Found')
|
58
75
|
socket.close
|
59
|
-
Thread.kill(thread)
|
60
|
-
thread.join
|
61
|
-
end
|
62
|
-
end
|
63
76
|
|
64
|
-
|
65
|
-
|
66
|
-
begin
|
67
|
-
@intranet = described_class.new(Intranet::Logger.new(Intranet::Logger::FATAL))
|
68
|
-
thread = Thread.new do
|
69
|
-
@intranet.start
|
70
|
-
end
|
71
|
-
while @intranet.instance_variable_get(:@server).status != :Running
|
72
|
-
end
|
73
|
-
|
74
|
-
socket = TCPSocket.new('localhost', @intranet.port)
|
75
|
-
socket.puts("GET /index.html HTTP/1.1\r\n" \
|
76
|
-
"Host: localhost:#{@intranet.port}\r\n\r\n")
|
77
|
-
expect(socket.gets).to include('HTTP/1.1 404 Not Found')
|
78
|
-
ensure
|
79
|
-
socket.close
|
80
|
-
Thread.kill(thread)
|
81
|
-
thread.join
|
82
|
-
end
|
77
|
+
@intranet.stop
|
78
|
+
thread&.join
|
83
79
|
end
|
84
80
|
end
|
85
81
|
end
|
@@ -87,58 +83,71 @@ RSpec.describe Intranet::Core do
|
|
87
83
|
describe '#register_module' do
|
88
84
|
context 'registering a module when the server is running' do
|
89
85
|
it 'should fail' do
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
thread = Thread.new do
|
94
|
-
@intranet.start
|
95
|
-
end
|
96
|
-
while @intranet.instance_variable_get(:@server).status != :Running
|
97
|
-
end
|
98
|
-
|
99
|
-
expect { @intranet.register_module(r, ['path'], '') }.to raise_error Errno::EALREADY
|
100
|
-
ensure
|
101
|
-
Thread.kill(thread)
|
102
|
-
thread.join
|
86
|
+
@intranet = described_class.new(Intranet::Logger.new(Intranet::Logger::FATAL))
|
87
|
+
thread = Thread.new { @intranet.start }
|
88
|
+
while @intranet.instance_variable_get(:@server).status != :Running
|
103
89
|
end
|
90
|
+
|
91
|
+
r = Intranet::TestResponder.new('/index.html' => [200, 'text/html', ''])
|
92
|
+
expect { @intranet.register_module(r, ['path'], '') }.to raise_error Errno::EALREADY
|
93
|
+
|
94
|
+
@intranet.stop
|
95
|
+
thread&.join
|
104
96
|
end
|
105
97
|
end
|
106
98
|
|
107
99
|
context 'registering a module with an invalid path' do
|
108
100
|
it 'should fail' do
|
109
101
|
@intranet = described_class.new(Intranet::Logger.new(Intranet::Logger::FATAL))
|
102
|
+
|
110
103
|
r = Intranet::TestResponder.new('/index.html' => [200, 'text/html', ''])
|
111
104
|
expect { @intranet.register_module(r, [], '') }.to raise_error ArgumentError
|
112
105
|
expect { @intranet.register_module(r, %w[1 2 3], '') }.to raise_error ArgumentError
|
113
106
|
expect { @intranet.register_module(r, ['', 'valid'], '') }.to raise_error ArgumentError
|
114
107
|
expect { @intranet.register_module(r, ['Invalid'], '') }.to raise_error ArgumentError
|
115
108
|
expect { @intranet.register_module(r, 'fo', '') }.to raise_error ArgumentError
|
109
|
+
|
110
|
+
thread = Thread.new { @intranet.start }
|
111
|
+
while @intranet.instance_variable_get(:@server).status != :Running
|
112
|
+
end
|
113
|
+
|
114
|
+
@intranet.stop
|
115
|
+
thread&.join
|
116
116
|
end
|
117
117
|
end
|
118
118
|
|
119
119
|
context 'registering an invalid module' do
|
120
120
|
it 'should fail' do
|
121
121
|
@intranet = described_class.new(Intranet::Logger.new(Intranet::Logger::FATAL))
|
122
|
+
|
122
123
|
expect { @intranet.register_module(nil, ['path'], '') }.to raise_error ArgumentError
|
124
|
+
|
125
|
+
thread = Thread.new { @intranet.start }
|
126
|
+
while @intranet.instance_variable_get(:@server).status != :Running
|
127
|
+
end
|
128
|
+
|
129
|
+
@intranet.stop
|
130
|
+
thread&.join
|
123
131
|
end
|
124
132
|
end
|
125
133
|
|
126
134
|
context 'when a valid module is registered' do
|
127
135
|
before(:each) do
|
128
136
|
@intranet = described_class.new(Intranet::Logger.new(Intranet::Logger::FATAL))
|
137
|
+
|
129
138
|
responder = Intranet::TestResponder.new('/index.html' => [200, 'text/html', ''])
|
139
|
+
# Third argument of register_module() is optional, so we test both cases.
|
130
140
|
@intranet.register_module(responder, %w[responder], responder.resources_dir)
|
131
|
-
@intranet.register_module(responder, %w[resp onder]
|
132
|
-
|
133
|
-
|
134
|
-
end
|
141
|
+
@intranet.register_module(responder, %w[resp onder])
|
142
|
+
|
143
|
+
@thread = Thread.new { @intranet.start }
|
135
144
|
while @intranet.instance_variable_get(:@server).status != :Running
|
136
145
|
end
|
137
146
|
end
|
138
147
|
|
139
148
|
after(:each) do
|
140
|
-
|
141
|
-
@thread
|
149
|
+
@intranet.stop
|
150
|
+
@thread&.join
|
142
151
|
end
|
143
152
|
|
144
153
|
it 'should be used to serve URI relative to the module root' do
|
@@ -177,157 +186,143 @@ RSpec.describe Intranet::Core do
|
|
177
186
|
end
|
178
187
|
|
179
188
|
context 'given a valid and registered module' do
|
180
|
-
it 'should be called with the URL path and query' do
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
break if line.empty?
|
197
|
-
end
|
198
|
-
line = socket.gets.chomp
|
199
|
-
expect(line).to eql({ 'var1' => 'value1', 'var2' => 'value2' }.to_s)
|
200
|
-
socket.close
|
201
|
-
|
202
|
-
socket = TCPSocket.new('localhost', @intranet.port)
|
203
|
-
socket.puts("GET /responder/query?foo=bar&baz=boz HTTP/1.1\r\n" \
|
204
|
-
"Host: localhost:#{@intranet.port}\r\n\r\n")
|
205
|
-
expect(socket.gets).to include('HTTP/1.1 200 OK')
|
206
|
-
while (line = socket.gets.chomp) # consume HTTP response headers
|
207
|
-
break if line.empty?
|
208
|
-
end
|
209
|
-
line = socket.gets.chomp
|
210
|
-
expect(line).to eql({ 'foo' => 'bar', 'baz' => 'boz' }.to_s)
|
211
|
-
ensure
|
212
|
-
socket.close
|
213
|
-
Thread.kill(thread)
|
214
|
-
thread.join
|
189
|
+
it 'should be called with the decoded URL path and query in UTF-8 encoding' do
|
190
|
+
@intranet = described_class.new(Intranet::Logger.new(Intranet::Logger::FATAL))
|
191
|
+
|
192
|
+
responder = Intranet::TestResponder.new('/index.html' => [200, 'text/html', ''])
|
193
|
+
@intranet.register_module(responder, ['responder'], responder.resources_dir)
|
194
|
+
|
195
|
+
thread = Thread.new { @intranet.start }
|
196
|
+
while @intranet.instance_variable_get(:@server).status != :Running
|
197
|
+
end
|
198
|
+
|
199
|
+
socket = TCPSocket.new('localhost', @intranet.port)
|
200
|
+
socket.puts("GET /responder/query%20t?var1=value1&var2=value2 HTTP/1.1\r\n" \
|
201
|
+
"Host: localhost:#{@intranet.port}\r\n\r\n")
|
202
|
+
expect(socket.gets).to include('HTTP/1.1 200 OK')
|
203
|
+
while (line = socket.gets.chomp) # consume HTTP response headers
|
204
|
+
break if line.empty?
|
215
205
|
end
|
206
|
+
line = socket.gets.chomp
|
207
|
+
expect(line).to eql(
|
208
|
+
'PATH=/query t (UTF-8), ' \
|
209
|
+
'QUERY={var1 (UTF-8) => value1 (UTF-8),var2 (UTF-8) => value2 (UTF-8)}'
|
210
|
+
)
|
211
|
+
socket.close
|
212
|
+
|
213
|
+
@intranet.stop
|
214
|
+
thread&.join
|
216
215
|
end
|
217
216
|
end
|
218
217
|
|
219
218
|
context 'given a module returning partial HTML content' do
|
220
219
|
it 'should be called to retrieve the body of the page' do
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
while @intranet.instance_variable_get(:@server).status != :Running
|
235
|
-
end
|
236
|
-
|
237
|
-
socket = TCPSocket.new('localhost', @intranet.port)
|
238
|
-
socket.puts("GET /r/index.html HTTP/1.1\r\nHost: localhost:#{@intranet.port}\r\n\r\n")
|
239
|
-
|
240
|
-
# Return code: HTTP error 200
|
241
|
-
expect(socket.gets).to include('HTTP/1.1 200 OK')
|
242
|
-
|
243
|
-
while (line = socket.gets.chomp) # consume HTTP response headers
|
244
|
-
break if line.empty?
|
245
|
-
end
|
246
|
-
html = socket.readpartial(4096) # read rest of data
|
247
|
-
|
248
|
-
# Returned HTML document: includes the partial content and the title
|
249
|
-
expect(html).to match(%r{<main>.*PARTIAL_CONTENT.*</main>}m)
|
250
|
-
expect(html).to match(%r{<head>.*<title>.*MyTitle.*</title>.*</head>}m)
|
251
|
-
|
252
|
-
# Returned HTML document: includes the hostname in title, h1-title and footer
|
253
|
-
hostname = Socket.gethostname
|
254
|
-
expect(html).to match(%r{<head>.*<title>.*#{hostname.capitalize}.*</title>.*</head>}m)
|
255
|
-
expect(html).to match(%r{<body>.*<h1>.*#{hostname.capitalize}.*</h1>.*</body>}m)
|
256
|
-
expect(html).to match(%r{<footer>.*#{hostname}.*</footer>}m)
|
257
|
-
|
258
|
-
# Returned HTML document: includes all CSS dependencies, relative or absolute path
|
259
|
-
expect(html).to match(%r{<link href='/responder.css' rel='stylesheet' type='text/css'})
|
260
|
-
expect(html).to match(%r{<link href='/r/nav.css' rel='stylesheet' type='text/css'})
|
261
|
-
|
262
|
-
# Returned HTML document: includes all JS dependencies
|
263
|
-
expect(html).to match(%r{<script defer='defer' src='/r/module.js'></script>})
|
264
|
-
expect(html).to match(%r{<script defer='defer' src='/js/interactive.js'></script>})
|
265
|
-
|
266
|
-
# Returned HTML document: includes Intranet Core name, version and URL
|
267
|
-
expect(html).to match(
|
268
|
-
%r{<footer>.*<a href='#{Intranet::Core::HOMEPAGE_URL}'.*>#{Intranet::Core::NAME}</a>.*#{Intranet::Core::VERSION}.*</footer>}m # rubocop:disable Metrics/LineLength
|
269
|
-
)
|
270
|
-
|
271
|
-
# Returned HTML document: includes all registered modules version name, version and URL
|
272
|
-
expect(html).to match(
|
273
|
-
%r{<footer>.*<a href='http://nil/'.*>test-responder</a>.*0.0.0.*</footer>}m
|
274
|
-
)
|
275
|
-
ensure
|
276
|
-
socket.close
|
277
|
-
Thread.kill(thread)
|
278
|
-
thread.join
|
220
|
+
@intranet = described_class.new(Intranet::Logger.new(Intranet::Logger::FATAL))
|
221
|
+
|
222
|
+
responder = Intranet::TestResponder.new(
|
223
|
+
{
|
224
|
+
'/index.html' => [206, 'text/html', { content: 'PARTIAL_CONTENT', title: 'MyTitle' }]
|
225
|
+
},
|
226
|
+
['/responder.css', 'nav.css'],
|
227
|
+
['module.js', '/js/interactive.js']
|
228
|
+
)
|
229
|
+
@intranet.register_module(responder, ['r'], responder.resources_dir)
|
230
|
+
|
231
|
+
thread = Thread.new { @intranet.start }
|
232
|
+
while @intranet.instance_variable_get(:@server).status != :Running
|
279
233
|
end
|
234
|
+
|
235
|
+
socket = TCPSocket.new('localhost', @intranet.port)
|
236
|
+
socket.puts("GET /r/index.html HTTP/1.1\r\nHost: localhost:#{@intranet.port}\r\n\r\n")
|
237
|
+
|
238
|
+
# Return code: HTTP error 200
|
239
|
+
expect(socket.gets).to include('HTTP/1.1 200 OK')
|
240
|
+
|
241
|
+
while (line = socket.gets.chomp) # consume HTTP response headers
|
242
|
+
break if line.empty?
|
243
|
+
end
|
244
|
+
html = socket.readpartial(4096) # read rest of data
|
245
|
+
socket.close
|
246
|
+
|
247
|
+
# Returned HTML document: includes the partial content and the title
|
248
|
+
expect(html).to match(%r{<main>.*PARTIAL_CONTENT.*</main>}m)
|
249
|
+
expect(html).to match(%r{<head>.*<title>.*MyTitle.*</title>.*</head>}m)
|
250
|
+
|
251
|
+
# Returned HTML document: includes the hostname in title, h1-title and footer
|
252
|
+
hostname = Socket.gethostname
|
253
|
+
expect(html).to match(%r{<head>.*<title>.*#{hostname.capitalize}.*</title>.*</head>}m)
|
254
|
+
expect(html).to match(%r{<body>.*<h1>.*#{hostname.capitalize}.*</h1>.*</body>}m)
|
255
|
+
expect(html).to match(%r{<footer>.*#{hostname}.*</footer>}m)
|
256
|
+
|
257
|
+
# Returned HTML document: includes all CSS dependencies, relative or absolute path
|
258
|
+
expect(html).to match(%r{<link href='/responder.css' rel='stylesheet' type='text/css'})
|
259
|
+
expect(html).to match(%r{<link href='/r/nav.css' rel='stylesheet' type='text/css'})
|
260
|
+
|
261
|
+
# Returned HTML document: includes all JS dependencies
|
262
|
+
expect(html).to match(%r{<script defer='defer' src='/r/module.js'></script>})
|
263
|
+
expect(html).to match(%r{<script defer='defer' src='/js/interactive.js'></script>})
|
264
|
+
|
265
|
+
# Returned HTML document: includes Intranet Core name, version and URL
|
266
|
+
expect(html).to match(
|
267
|
+
%r{<footer>.*<a href='#{Intranet::Core::HOMEPAGE_URL}'.*>#{Intranet::Core::NAME}</a>.*#{Intranet::Core::VERSION}.*</footer>}m # rubocop:disable Layout/LineLength
|
268
|
+
)
|
269
|
+
|
270
|
+
# Returned HTML document: includes all registered modules version name, version and URL
|
271
|
+
expect(html).to match(
|
272
|
+
%r{<footer>.*<a href='http://nil/'.*>test-responder</a>.*0.0.0.*</footer>}m
|
273
|
+
)
|
274
|
+
|
275
|
+
@intranet.stop
|
276
|
+
thread&.join
|
280
277
|
end
|
281
278
|
it 'should be called to update the main navigation menu' do
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
while @intranet.instance_variable_get(:@server).status != :Running
|
300
|
-
end
|
301
|
-
|
302
|
-
socket = TCPSocket.new('localhost', @intranet.port)
|
303
|
-
socket.puts("GET /r/index.html HTTP/1.1\r\nHost: localhost:#{@intranet.port}\r\n\r\n")
|
304
|
-
|
305
|
-
# Return code: HTTP error 200
|
306
|
-
expect(socket.gets).to include('HTTP/1.1 200 OK')
|
307
|
-
|
308
|
-
while (line = socket.gets.chomp)
|
309
|
-
break if line.empty?
|
310
|
-
end
|
311
|
-
html = socket.readpartial(4096) # read rest of data
|
312
|
-
|
313
|
-
# Returned HTML document main menu
|
314
|
-
expect(html).to match(%r{<a href='/dep_th1/index.html'>.*Dep Th1.*</a>})
|
315
|
-
expect(html).not_to match(%r{<a href='/other1/index.html'>})
|
316
|
-
expect(html).to match(
|
317
|
-
%r{<a>.*Depth2.*</a>.*<ul>.*<a href='/depth2/res_p1/index.html'>.*Res P1.*</a>.*</ul>}m
|
318
|
-
)
|
319
|
-
expect(html).to match(
|
320
|
-
%r{<a>.*Depth2.*</a>.*<ul>.*<a href='/depth2/resp2/index.html'>.*Resp2.*</a>.*</ul>}m
|
321
|
-
)
|
322
|
-
expect(html).not_to match(%r{<a href='/depth2/resp/index.html'>})
|
323
|
-
expect(html).not_to match(%r{<a>.*Other2.*</a>}m)
|
324
|
-
expect(html).not_to match(%r{<a href='/other2/res1/index.html'>})
|
325
|
-
expect(html).not_to match(%r{<a href='/other2/res2/index.html'>})
|
326
|
-
ensure
|
327
|
-
socket.close
|
328
|
-
Thread.kill(thread)
|
329
|
-
thread.join
|
279
|
+
@intranet = described_class.new(Intranet::Logger.new(Intranet::Logger::FATAL))
|
280
|
+
|
281
|
+
responder = Intranet::TestResponder.new(
|
282
|
+
'/index.html' => [206, 'text/html', { content: 'PARTIAL_CONTENT', title: 'MyTitle' }]
|
283
|
+
)
|
284
|
+
other_responder = Intranet::TestResponder.new({}, [], [], true)
|
285
|
+
@intranet.register_module(responder, %w[r], responder.resources_dir)
|
286
|
+
@intranet.register_module(responder, %w[dep_th1], responder.resources_dir)
|
287
|
+
@intranet.register_module(responder, %w[depth2 res_p1], responder.resources_dir)
|
288
|
+
@intranet.register_module(responder, %w[depth2 resp2], responder.resources_dir)
|
289
|
+
@intranet.register_module(other_responder, %w[depth2 resp], other_responder.resources_dir)
|
290
|
+
@intranet.register_module(other_responder, %w[other1], other_responder.resources_dir)
|
291
|
+
@intranet.register_module(other_responder, %w[other2 res1], other_responder.resources_dir)
|
292
|
+
@intranet.register_module(other_responder, %w[other2 res2], other_responder.resources_dir)
|
293
|
+
|
294
|
+
thread = Thread.new { @intranet.start }
|
295
|
+
while @intranet.instance_variable_get(:@server).status != :Running
|
330
296
|
end
|
297
|
+
|
298
|
+
socket = TCPSocket.new('localhost', @intranet.port)
|
299
|
+
socket.puts("GET /r/index.html HTTP/1.1\r\nHost: localhost:#{@intranet.port}\r\n\r\n")
|
300
|
+
|
301
|
+
# Return code: HTTP error 200
|
302
|
+
expect(socket.gets).to include('HTTP/1.1 200 OK')
|
303
|
+
|
304
|
+
while (line = socket.gets.chomp)
|
305
|
+
break if line.empty?
|
306
|
+
end
|
307
|
+
html = socket.readpartial(4096) # read rest of data
|
308
|
+
socket.close
|
309
|
+
|
310
|
+
# Returned HTML document main menu
|
311
|
+
expect(html).to match(%r{<a href='/dep_th1/index.html'>.*Dep Th1.*</a>})
|
312
|
+
expect(html).not_to match(%r{<a href='/other1/index.html'>})
|
313
|
+
expect(html).to match(
|
314
|
+
%r{<a>.*Depth2.*</a>.*<ul>.*<a href='/depth2/res_p1/index.html'>.*Res P1.*</a>.*</ul>}m
|
315
|
+
)
|
316
|
+
expect(html).to match(
|
317
|
+
%r{<a>.*Depth2.*</a>.*<ul>.*<a href='/depth2/resp2/index.html'>.*Resp2.*</a>.*</ul>}m
|
318
|
+
)
|
319
|
+
expect(html).not_to match(%r{<a href='/depth2/resp/index.html'>})
|
320
|
+
expect(html).not_to match(%r{<a>.*Other2.*</a>}m)
|
321
|
+
expect(html).not_to match(%r{<a href='/other2/res1/index.html'>})
|
322
|
+
expect(html).not_to match(%r{<a href='/other2/res2/index.html'>})
|
323
|
+
|
324
|
+
@intranet.stop
|
325
|
+
thread&.join
|
331
326
|
end
|
332
327
|
end
|
333
328
|
end
|
@@ -336,66 +331,61 @@ RSpec.describe Intranet::Core do
|
|
336
331
|
context 'given a relative URL' do
|
337
332
|
it 'should fail' do
|
338
333
|
@intranet = described_class.new(Intranet::Logger.new(Intranet::Logger::FATAL))
|
334
|
+
|
339
335
|
expect { @intranet.home_url = 'foo/index.html' }.to raise_error ArgumentError
|
340
|
-
end
|
341
|
-
end
|
342
336
|
|
343
|
-
|
344
|
-
|
345
|
-
begin
|
346
|
-
@intranet = described_class.new(Intranet::Logger.new(Intranet::Logger::FATAL))
|
347
|
-
@intranet.home_url = '/responder/index.html'
|
348
|
-
thread = Thread.new do
|
349
|
-
@intranet.start
|
350
|
-
end
|
351
|
-
while @intranet.instance_variable_get(:@server).status != :Running
|
352
|
-
end
|
353
|
-
|
354
|
-
socket = TCPSocket.new('localhost', @intranet.port)
|
355
|
-
socket.puts("GET /index.html HTTP/1.1\r\nHost: localhost:#{@intranet.port}\r\n\r\n")
|
356
|
-
expect(socket.gets).to include('HTTP/1.1 307 Temporary Redirect')
|
357
|
-
while (line = socket.gets.chomp) # search the HTTP response for the 'Location' header
|
358
|
-
break if line.start_with?('Location:')
|
359
|
-
end
|
360
|
-
expect(line).to include("http://localhost:#{@intranet.port}/responder/index.html")
|
361
|
-
ensure
|
362
|
-
socket.close
|
363
|
-
Thread.kill(thread)
|
364
|
-
thread.join
|
337
|
+
thread = Thread.new { @intranet.start }
|
338
|
+
while @intranet.instance_variable_get(:@server).status != :Running
|
365
339
|
end
|
340
|
+
|
341
|
+
@intranet.stop
|
342
|
+
thread&.join
|
366
343
|
end
|
367
344
|
end
|
368
|
-
end
|
369
345
|
|
370
|
-
|
371
|
-
|
372
|
-
begin
|
346
|
+
context 'given an absolute URL' do
|
347
|
+
it 'should set up a redirection from /index.html to the provided URL' do
|
373
348
|
@intranet = described_class.new(Intranet::Logger.new(Intranet::Logger::FATAL))
|
374
|
-
|
375
|
-
@intranet.
|
376
|
-
thread = Thread.new do
|
377
|
-
@intranet.start
|
378
|
-
end
|
349
|
+
@intranet.home_url = '/responder/index.html'
|
350
|
+
thread = Thread.new { @intranet.start }
|
379
351
|
while @intranet.instance_variable_get(:@server).status != :Running
|
380
352
|
end
|
381
353
|
|
382
354
|
socket = TCPSocket.new('localhost', @intranet.port)
|
383
|
-
socket.puts("GET /
|
384
|
-
expect(socket.gets).to include('HTTP/1.1
|
385
|
-
|
355
|
+
socket.puts("GET /index.html HTTP/1.1\r\nHost: localhost:#{@intranet.port}\r\n\r\n")
|
356
|
+
expect(socket.gets).to include('HTTP/1.1 307 Temporary Redirect')
|
357
|
+
while (line = socket.gets.chomp) # search the HTTP response for the 'Location' header
|
358
|
+
break if line.start_with?('Location:')
|
359
|
+
end
|
360
|
+
expect(line).to include("http://localhost:#{@intranet.port}/responder/index.html")
|
386
361
|
socket.close
|
387
362
|
|
388
363
|
@intranet.stop
|
389
|
-
|
390
|
-
|
364
|
+
thread&.join
|
365
|
+
end
|
366
|
+
end
|
367
|
+
end
|
391
368
|
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
369
|
+
describe '#stop' do
|
370
|
+
it 'should stop the web server and finalize all registered responders' do
|
371
|
+
@intranet = described_class.new(Intranet::Logger.new(Intranet::Logger::FATAL))
|
372
|
+
responder = Intranet::TestResponder.new('/index.html' => [200, 'text/html', 'CONTENT'])
|
373
|
+
@intranet.register_module(responder, %w[r], responder.resources_dir)
|
374
|
+
thread = Thread.new { @intranet.start }
|
375
|
+
while @intranet.instance_variable_get(:@server).status != :Running
|
398
376
|
end
|
377
|
+
|
378
|
+
socket = TCPSocket.new('localhost', @intranet.port)
|
379
|
+
socket.puts("GET /r/index.html HTTP/1.1\r\nHost: localhost:#{@intranet.port}\r\n\r\n")
|
380
|
+
expect(socket.gets).to include('HTTP/1.1 200 OK')
|
381
|
+
expect(responder.finalized).to be false
|
382
|
+
socket.close
|
383
|
+
|
384
|
+
@intranet.stop
|
385
|
+
thread&.join
|
386
|
+
|
387
|
+
expect { TCPSocket.new('localhost', @intranet.port) }.to raise_error(Errno::ECONNREFUSED)
|
388
|
+
expect(responder.finalized).to be true
|
399
389
|
end
|
400
390
|
end
|
401
391
|
end
|
@@ -38,12 +38,12 @@ module Intranet
|
|
38
38
|
end
|
39
39
|
|
40
40
|
def resources_dir
|
41
|
-
|
41
|
+
super
|
42
42
|
end
|
43
43
|
|
44
44
|
def generate_page(path, query)
|
45
|
-
if path
|
46
|
-
[200, 'text/plain', query
|
45
|
+
if path.start_with?('/query')
|
46
|
+
[200, 'text/plain', dump_with_encoding(path, query)]
|
47
47
|
else
|
48
48
|
@responses.fetch(path)
|
49
49
|
end
|
@@ -58,5 +58,13 @@ module Intranet
|
|
58
58
|
def js_dependencies
|
59
59
|
super + @extra_js
|
60
60
|
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
def dump_with_encoding(path, query)
|
65
|
+
"PATH=#{path} (#{path.encoding}), QUERY={" +
|
66
|
+
query.map { |k, v| "#{k} (#{k.encoding}) => #{v} (#{v.encoding})" }.join(',') +
|
67
|
+
"}\r\n"
|
68
|
+
end
|
61
69
|
end
|
62
70
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: intranet-core
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ebling Mis
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-01-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: haml
|
@@ -114,14 +114,14 @@ dependencies:
|
|
114
114
|
requirements:
|
115
115
|
- - "~>"
|
116
116
|
- !ruby/object:Gem::Version
|
117
|
-
version: 0.
|
117
|
+
version: 0.81.0
|
118
118
|
type: :development
|
119
119
|
prerelease: false
|
120
120
|
version_requirements: !ruby/object:Gem::Requirement
|
121
121
|
requirements:
|
122
122
|
- - "~>"
|
123
123
|
- !ruby/object:Gem::Version
|
124
|
-
version: 0.
|
124
|
+
version: 0.81.0
|
125
125
|
- !ruby/object:Gem::Dependency
|
126
126
|
name: simplecov
|
127
127
|
requirement: !ruby/object:Gem::Requirement
|
@@ -158,6 +158,7 @@ extra_rdoc_files: []
|
|
158
158
|
files:
|
159
159
|
- README.md
|
160
160
|
- lib/core_extensions.rb
|
161
|
+
- lib/core_extensions/hash.rb
|
161
162
|
- lib/core_extensions/string.rb
|
162
163
|
- lib/core_extensions/tree.rb
|
163
164
|
- lib/core_extensions/webrick/httpresponse.rb
|
@@ -179,6 +180,7 @@ files:
|
|
179
180
|
- lib/intranet/resources/www/fonts/SourceSansPro.woff2
|
180
181
|
- lib/intranet/resources/www/nav.js
|
181
182
|
- lib/intranet/resources/www/style.css
|
183
|
+
- spec/core_extensions/hash_spec.rb
|
182
184
|
- spec/core_extensions/string_spec.rb
|
183
185
|
- spec/core_extensions/tree_spec.rb
|
184
186
|
- spec/core_extensions/webrick/httpresponse_spec.rb
|
@@ -189,7 +191,6 @@ files:
|
|
189
191
|
- spec/intranet/logger_spec.rb
|
190
192
|
- spec/spec_helper.rb
|
191
193
|
- spec/test_responder/responder.rb
|
192
|
-
- spec/test_responder/www/style.css
|
193
194
|
homepage: https://rubygems.org/gems/intranet-core
|
194
195
|
licenses:
|
195
196
|
- MIT
|
@@ -211,7 +212,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
211
212
|
version: '0'
|
212
213
|
requirements: []
|
213
214
|
rubyforge_project:
|
214
|
-
rubygems_version: 2.
|
215
|
+
rubygems_version: 2.7.6.2
|
215
216
|
signing_key:
|
216
217
|
specification_version: 4
|
217
218
|
summary: Core component to build a custom intranet.
|