intranet-core 1.2.0 → 2.1.4
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/intranet/abstract_responder.rb +17 -4
- data/lib/intranet/core.rb +18 -12
- data/lib/intranet/core/builder.rb +36 -12
- data/lib/intranet/core/servlet.rb +25 -4
- data/lib/intranet/core/version.rb +1 -1
- data/lib/intranet/resources/haml/skeleton.haml +8 -2
- data/lib/intranet/resources/www/nav.js +2 -1
- data/lib/intranet/resources/www/style.css +0 -1
- data/spec/intranet/core_spec.rb +107 -114
- data/spec/test_responder/responder.rb +11 -3
- metadata +5 -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: e1d7d91fa0a962e4835681a46f50c7e49b5aaa77327ab112d2999aa90b26a312
|
|
4
|
+
data.tar.gz: 3c67a863896c42b5b83de33863e372678bae92ee831c05f91a6484343ecff88e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2df2d7de87df4c1a267c6fa39f3188ec8231b265c7206351bcde3a1203ad4c4c5d6b163a747888f1967e783c8fd1e1d0a1bbf5d081bd8d5f864eee83ae67ef03
|
|
7
|
+
data.tar.gz: 0ca99259824749ffe9f08c3c8c32a0faa7b7f6afaa063c5e45072fe883355ed684beb35af5ab878f94ff2a037ca391bb9a4a05c918ec3e31ea9e817b716531b7
|
|
@@ -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
|
|
@@ -36,16 +45,20 @@ module Intranet
|
|
|
36
45
|
[404, '', '']
|
|
37
46
|
end
|
|
38
47
|
|
|
39
|
-
# Provides the list of Cascade Style Sheets (CSS) dependencies for this module
|
|
48
|
+
# Provides the list of Cascade Style Sheets (CSS) dependencies for this module, either using
|
|
49
|
+
# absolute or relative (from the module root) paths.
|
|
40
50
|
# If redefined, this method should probably append dependencies rather than overwriting them.
|
|
41
|
-
# @return [Array] The list of CSS dependencies, as
|
|
51
|
+
# @return [Array] The list of CSS dependencies, as absolute path or relative to the module root
|
|
52
|
+
# URL.
|
|
42
53
|
def css_dependencies
|
|
43
54
|
['/design/style.css']
|
|
44
55
|
end
|
|
45
56
|
|
|
46
|
-
# Provides the list of Javascript files (JS) dependencies for this module
|
|
57
|
+
# Provides the list of Javascript files (JS) dependencies for this module, either using
|
|
58
|
+
# absolute or relative (from the module root) paths.
|
|
47
59
|
# If redefined, this method should probably append dependencies rather than overwriting them.
|
|
48
|
-
# @return [Array] The list of JS dependencies, as
|
|
60
|
+
# @return [Array] The list of JS dependencies, as absolute path or relative to the module root
|
|
61
|
+
# URL.
|
|
49
62
|
def js_dependencies
|
|
50
63
|
['/design/nav.js']
|
|
51
64
|
end
|
data/lib/intranet/core.rb
CHANGED
|
@@ -40,22 +40,19 @@ module Intranet
|
|
|
40
40
|
mount_default_servlets(www_dir)
|
|
41
41
|
end
|
|
42
42
|
|
|
43
|
-
# Registers a new module to the core. The server must not be running.
|
|
44
|
-
#
|
|
45
|
-
# given +path+ under the web server root.
|
|
43
|
+
# Registers a new module to the core. The server must not be running. The module will then be
|
|
44
|
+
# accessible through the given +path+ under the web server root.
|
|
46
45
|
# @param responder [Intranet::AbstractResponder] The module responder instance.
|
|
47
46
|
# @param path [Array] The path, relative to the web server root, representing the module root
|
|
48
|
-
# directory.
|
|
49
|
-
#
|
|
50
|
-
# an array element for each directory level. Each element must only contain
|
|
51
|
-
# the following characters: +-_a-z0-9+.
|
|
47
|
+
# directory. The path cannot be empty and have more than 2 elements. Each
|
|
48
|
+
# element must only contain the following characters: +-_a-z0-9+.
|
|
52
49
|
# @param resources_dir [String] The path to the directory that contains additional resources
|
|
53
50
|
# required by the module. This directory should contain three
|
|
54
|
-
# subdirectories: +haml/+, +locales/+ and +www/+.
|
|
55
|
-
#
|
|
51
|
+
# subdirectories: +haml/+, +locales/+ and +www/+. The +www/+ is
|
|
52
|
+
# mounted under +<path>/design+ on the web server.
|
|
53
|
+
# @raise [ArgumentError] If the +path+ is not valid.
|
|
56
54
|
# @raise [Errno::EALREADY] If the server is already running.
|
|
57
|
-
def register_module(responder, path, resources_dir)
|
|
58
|
-
raise ArgumentError unless path.all?(&:urlized?)
|
|
55
|
+
def register_module(responder, path, resources_dir = responder.resources_dir)
|
|
59
56
|
raise Errno::EALREADY if @server.status != :Stop
|
|
60
57
|
|
|
61
58
|
@builder.register(responder, path)
|
|
@@ -64,6 +61,13 @@ module Intranet
|
|
|
64
61
|
module_add_haml_templates(resources_dir)
|
|
65
62
|
end
|
|
66
63
|
|
|
64
|
+
# Defines the URL of the home page.
|
|
65
|
+
# @param url [String] The absolute URL of the home page.
|
|
66
|
+
# @raise [ArgumentError] If the URL is not an absolute path.
|
|
67
|
+
def home_url=(url)
|
|
68
|
+
@builder.home_url = url
|
|
69
|
+
end
|
|
70
|
+
|
|
67
71
|
# Starts the web server.
|
|
68
72
|
def start
|
|
69
73
|
@logger.info('Intranet::Core: using locale \'' + I18n.default_locale.to_s + '\'')
|
|
@@ -76,6 +80,8 @@ module Intranet
|
|
|
76
80
|
def stop
|
|
77
81
|
@logger.info('Intranet::Runner: requesting system shutdown...')
|
|
78
82
|
@server.shutdown
|
|
83
|
+
while @server.status != :Stop
|
|
84
|
+
end
|
|
79
85
|
@builder.finalize
|
|
80
86
|
@logger.close
|
|
81
87
|
end
|
|
@@ -109,7 +115,7 @@ module Intranet
|
|
|
109
115
|
end
|
|
110
116
|
|
|
111
117
|
def module_add_servlet(path, resources_dir)
|
|
112
|
-
@server.mount('/
|
|
118
|
+
@server.mount('/' + path.join('/') + '/design',
|
|
113
119
|
WEBrick::HTTPServlet::FileHandler,
|
|
114
120
|
File.join(resources_dir, 'www'))
|
|
115
121
|
end
|
|
@@ -20,12 +20,17 @@ module Intranet
|
|
|
20
20
|
# @return [Hash]
|
|
21
21
|
attr_reader :modules
|
|
22
22
|
|
|
23
|
+
# The Home page URL
|
|
24
|
+
# @return [String]
|
|
25
|
+
attr_reader :home_url
|
|
26
|
+
|
|
23
27
|
# Initializes a new builder.
|
|
24
28
|
# @param logger [Object] The logger.
|
|
25
29
|
def initialize(logger)
|
|
26
30
|
@logger = logger
|
|
27
31
|
@responders = CoreExtensions::Tree.new
|
|
28
32
|
@modules = { NAME => { version: VERSION, homepage: HOMEPAGE_URL } }
|
|
33
|
+
@home_url = '/index.html'
|
|
29
34
|
end
|
|
30
35
|
|
|
31
36
|
# Finalizes the builder. Each registered responder is called for +finalize+.
|
|
@@ -47,13 +52,13 @@ module Intranet
|
|
|
47
52
|
# @param query [Hash] The content of the GET parameters of the URL.
|
|
48
53
|
# @return [Array] The HTTP return code, the MIME type and the answer body.
|
|
49
54
|
def do_get(path, query = {})
|
|
50
|
-
responder,
|
|
51
|
-
status, mime_type, body = responder.generate_page(
|
|
55
|
+
responder, parsed_path, unparsed_path = get_responder(path)
|
|
56
|
+
status, mime_type, body = responder.generate_page(unparsed_path, query)
|
|
52
57
|
|
|
53
58
|
# Generate header and footer when partial content is returned by the responder
|
|
54
59
|
if status == 206 && mime_type == 'text/html'
|
|
55
60
|
body = to_markup('skeleton', body: body, css: responder.css_dependencies,
|
|
56
|
-
js: responder.js_dependencies)
|
|
61
|
+
js: responder.js_dependencies, current_path: parsed_path)
|
|
57
62
|
status = 200
|
|
58
63
|
end
|
|
59
64
|
[status, mime_type, body]
|
|
@@ -65,35 +70,54 @@ module Intranet
|
|
|
65
70
|
# one overrides the old one.
|
|
66
71
|
# @param responder [Intranet::AbstractResponder] The responder instance of the module.
|
|
67
72
|
# @param path [Array] The path, relative to the web server root, representing the module root
|
|
68
|
-
# directory.
|
|
69
|
-
# (to serve /index.html in particular). Subdirectories are allowed using
|
|
70
|
-
# an array element for each directory level.
|
|
73
|
+
# directory.
|
|
71
74
|
# @raise [ArgumentError] If the +responder+ is not a valid responder instance.
|
|
72
|
-
# @raise [ArgumentError] If
|
|
75
|
+
# @raise [ArgumentError] If the +path+ is invalid.
|
|
73
76
|
def register(responder, path = [])
|
|
74
77
|
raise ArgumentError unless responder.class.superclass.to_s == 'Intranet::AbstractResponder'
|
|
75
|
-
raise ArgumentError unless
|
|
78
|
+
raise ArgumentError unless valid_path?(path)
|
|
76
79
|
|
|
77
80
|
insert_responder(responder, path)
|
|
78
81
|
store_module_metadata(responder)
|
|
79
82
|
end
|
|
80
83
|
|
|
84
|
+
# Defines the URL of the home page.
|
|
85
|
+
# @param url [String] The absolute URL of the home page.
|
|
86
|
+
# @raise [ArgumentError] If the URL is not an absolute path.
|
|
87
|
+
def home_url=(url)
|
|
88
|
+
raise ArgumentError unless url.start_with?('/')
|
|
89
|
+
|
|
90
|
+
@home_url = url
|
|
91
|
+
end
|
|
92
|
+
|
|
81
93
|
private
|
|
82
94
|
|
|
83
95
|
# Get the responder instance associated with the given path.
|
|
84
96
|
# @param path [String] The absolute URL path.
|
|
85
|
-
# @return [
|
|
86
|
-
#
|
|
97
|
+
# @return [Intranet::AbstractResponder, String, String] The responder instance (possibly nil)
|
|
98
|
+
# the parsed part of the path, and the
|
|
99
|
+
# unparsed part of the path.
|
|
87
100
|
def get_responder(path)
|
|
101
|
+
parsed_path = []
|
|
88
102
|
current_treenode = @responders
|
|
89
|
-
|
|
103
|
+
unparsed_path = path[1..-1].split('/').delete_if do |part|
|
|
90
104
|
if current_treenode.child_exists?(part)
|
|
91
105
|
current_treenode = current_treenode.child_node(part)
|
|
106
|
+
parsed_path << part
|
|
92
107
|
true
|
|
93
108
|
end
|
|
94
109
|
end
|
|
95
110
|
|
|
96
|
-
[current_treenode.value, '/' +
|
|
111
|
+
[current_treenode.value, '/' + parsed_path.join('/'), '/' + unparsed_path.join('/')]
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# Check whether a path is valid.
|
|
115
|
+
# @param path [Array] The path to test.
|
|
116
|
+
# @return [Boolean] True if the path is valid, False otherwise.
|
|
117
|
+
def valid_path?(path)
|
|
118
|
+
(path.size == 1 || path.size == 2) && path.none?(&:empty?) && path.all?(&:urlized?)
|
|
119
|
+
rescue NoMethodError
|
|
120
|
+
false
|
|
97
121
|
end
|
|
98
122
|
|
|
99
123
|
# Inserts a responder instance in the appropriate Tree node according the given +path+.
|
|
@@ -24,12 +24,14 @@ module Intranet
|
|
|
24
24
|
def do_GET(request, response) # rubocop:disable Naming/MethodName
|
|
25
25
|
# See https://github.com/nahi/webrick/blob/master/lib/webrick/httprequest.rb
|
|
26
26
|
# We could use request.request_uri (which is a URI object) but its path is not normalized
|
|
27
|
-
# (it may contain '../' or event start with '..'). Hence we use request.
|
|
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
|
|
30
|
-
path += 'index.html' if path
|
|
29
|
+
path = request.path.force_encoding('UTF-8')
|
|
30
|
+
path += 'index.html' if path.end_with?('/')
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
handle_redirections(request, path, response, '/index.html' => @builder.home_url)
|
|
33
|
+
|
|
34
|
+
status, content_type, body = @builder.do_get(path, encode_query(request.query))
|
|
33
35
|
|
|
34
36
|
raise WEBrick::HTTPStatus[status] if WEBrick::HTTPStatus.error?(status)
|
|
35
37
|
|
|
@@ -37,6 +39,25 @@ module Intranet
|
|
|
37
39
|
response.content_type = content_type
|
|
38
40
|
response.body = body
|
|
39
41
|
end
|
|
42
|
+
|
|
43
|
+
private
|
|
44
|
+
|
|
45
|
+
# Issues an HTTP redirection (307) in +response+ if the +path+ associated to the +request+
|
|
46
|
+
# has an entry if the redirection table +redirects+.
|
|
47
|
+
def handle_redirections(request, path, response, redirects)
|
|
48
|
+
target = redirects.fetch(path)
|
|
49
|
+
location = URI.join(request.request_uri, target).to_s
|
|
50
|
+
response.set_redirect(WEBrick::HTTPStatus[307], location) if path != target
|
|
51
|
+
rescue KeyError
|
|
52
|
+
# nothing to do
|
|
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
|
|
40
61
|
end
|
|
41
62
|
end
|
|
42
63
|
end
|
|
@@ -5,9 +5,15 @@
|
|
|
5
5
|
%meta{charset: 'utf-8'}
|
|
6
6
|
%link{rel: 'icon', type: 'image/x-icon', href: '/design/favicon.ico'}
|
|
7
7
|
- css.each do |url|
|
|
8
|
-
|
|
8
|
+
- if url.start_with?('/')
|
|
9
|
+
%link{rel: 'stylesheet', type: 'text/css', href: url}
|
|
10
|
+
- else
|
|
11
|
+
%link{rel: 'stylesheet', type: 'text/css', href: current_path + '/' + url}
|
|
9
12
|
- js.each do |url|
|
|
10
|
-
|
|
13
|
+
- if url.start_with?('/')
|
|
14
|
+
%script{src: url, defer: 'defer'}
|
|
15
|
+
- else
|
|
16
|
+
%script{src: current_path + '/' + url, defer: 'defer'}
|
|
11
17
|
%body
|
|
12
18
|
%header
|
|
13
19
|
%a{id: 'openmenu', onclick: 'openNavMenu();'}= '☰'
|
|
@@ -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() {
|
data/spec/intranet/core_spec.rb
CHANGED
|
@@ -82,18 +82,21 @@ RSpec.describe Intranet::Core do
|
|
|
82
82
|
end
|
|
83
83
|
end
|
|
84
84
|
end
|
|
85
|
+
end
|
|
85
86
|
|
|
87
|
+
describe '#register_module' do
|
|
86
88
|
context 'registering a module when the server is running' do
|
|
87
89
|
it 'should fail' do
|
|
88
90
|
begin
|
|
89
91
|
@intranet = described_class.new(Intranet::Logger.new(Intranet::Logger::FATAL))
|
|
92
|
+
r = Intranet::TestResponder.new('/index.html' => [200, 'text/html', ''])
|
|
90
93
|
thread = Thread.new do
|
|
91
94
|
@intranet.start
|
|
92
95
|
end
|
|
93
96
|
while @intranet.instance_variable_get(:@server).status != :Running
|
|
94
97
|
end
|
|
95
98
|
|
|
96
|
-
expect { @intranet.register_module(
|
|
99
|
+
expect { @intranet.register_module(r, ['path'], '') }.to raise_error Errno::EALREADY
|
|
97
100
|
ensure
|
|
98
101
|
Thread.kill(thread)
|
|
99
102
|
thread.join
|
|
@@ -104,119 +107,82 @@ RSpec.describe Intranet::Core do
|
|
|
104
107
|
context 'registering a module with an invalid path' do
|
|
105
108
|
it 'should fail' do
|
|
106
109
|
@intranet = described_class.new(Intranet::Logger.new(Intranet::Logger::FATAL))
|
|
107
|
-
|
|
110
|
+
r = Intranet::TestResponder.new('/index.html' => [200, 'text/html', ''])
|
|
111
|
+
expect { @intranet.register_module(r, [], '') }.to raise_error ArgumentError
|
|
112
|
+
expect { @intranet.register_module(r, %w[1 2 3], '') }.to raise_error ArgumentError
|
|
113
|
+
expect { @intranet.register_module(r, ['', 'valid'], '') }.to raise_error ArgumentError
|
|
114
|
+
expect { @intranet.register_module(r, ['Invalid'], '') }.to raise_error ArgumentError
|
|
115
|
+
expect { @intranet.register_module(r, 'fo', '') }.to raise_error ArgumentError
|
|
108
116
|
end
|
|
109
117
|
end
|
|
110
118
|
|
|
111
119
|
context 'registering an invalid module' do
|
|
112
120
|
it 'should fail' do
|
|
113
121
|
@intranet = described_class.new(Intranet::Logger.new(Intranet::Logger::FATAL))
|
|
114
|
-
expect { @intranet.register_module(nil, [], '') }.to raise_error ArgumentError
|
|
122
|
+
expect { @intranet.register_module(nil, ['path'], '') }.to raise_error ArgumentError
|
|
115
123
|
end
|
|
116
124
|
end
|
|
117
125
|
|
|
118
|
-
context 'when a valid module is registered
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
end
|
|
130
|
-
|
|
131
|
-
socket = TCPSocket.new('localhost', @intranet.port)
|
|
132
|
-
socket.puts("GET /index.html HTTP/1.1\r\n" \
|
|
133
|
-
"Host: localhost:#{@intranet.port}\r\n\r\n")
|
|
134
|
-
expect(socket.gets).to include('HTTP/1.1 200 OK')
|
|
135
|
-
socket.close
|
|
136
|
-
|
|
137
|
-
socket = TCPSocket.new('localhost', @intranet.port)
|
|
138
|
-
socket.puts("GET /folder/index.html HTTP/1.1\r\n" \
|
|
139
|
-
"Host: localhost:#{@intranet.port}\r\n\r\n")
|
|
140
|
-
expect(socket.gets).to include('HTTP/1.1 200 OK')
|
|
141
|
-
socket.close
|
|
142
|
-
|
|
143
|
-
socket = TCPSocket.new('localhost', @intranet.port)
|
|
144
|
-
socket.puts("GET /folder/subdir/index.html HTTP/1.1\r\n" \
|
|
145
|
-
"Host: localhost:#{@intranet.port}\r\n\r\n")
|
|
146
|
-
expect(socket.gets).to include('HTTP/1.1 404 Not Found')
|
|
147
|
-
socket.close
|
|
148
|
-
|
|
149
|
-
socket = TCPSocket.new('localhost', @intranet.port)
|
|
150
|
-
socket.puts("GET /design/home/style.css HTTP/1.1\r\n" \
|
|
151
|
-
"Host: localhost:#{@intranet.port}\r\n\r\n")
|
|
152
|
-
expect(socket.gets).to include('HTTP/1.1 200 OK')
|
|
153
|
-
ensure
|
|
154
|
-
socket.close
|
|
155
|
-
Thread.kill(thread)
|
|
156
|
-
thread.join
|
|
126
|
+
context 'when a valid module is registered' do
|
|
127
|
+
before(:each) do
|
|
128
|
+
@intranet = described_class.new(Intranet::Logger.new(Intranet::Logger::FATAL))
|
|
129
|
+
responder = Intranet::TestResponder.new('/index.html' => [200, 'text/html', ''])
|
|
130
|
+
# Third argument of register_module() is optional, so we test both cases.
|
|
131
|
+
@intranet.register_module(responder, %w[responder], responder.resources_dir)
|
|
132
|
+
@intranet.register_module(responder, %w[resp onder])
|
|
133
|
+
@thread = Thread.new do
|
|
134
|
+
@intranet.start
|
|
135
|
+
end
|
|
136
|
+
while @intranet.instance_variable_get(:@server).status != :Running
|
|
157
137
|
end
|
|
158
138
|
end
|
|
159
|
-
end
|
|
160
139
|
|
|
161
|
-
|
|
140
|
+
after(:each) do
|
|
141
|
+
Thread.kill(@thread)
|
|
142
|
+
@thread.join
|
|
143
|
+
end
|
|
144
|
+
|
|
162
145
|
it 'should be used to serve URI relative to the module root' do
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
@intranet.register_module(responder, %w[resp onder], responder.resources_dir)
|
|
169
|
-
thread = Thread.new do
|
|
170
|
-
@intranet.start
|
|
171
|
-
end
|
|
172
|
-
while @intranet.instance_variable_get(:@server).status != :Running
|
|
173
|
-
end
|
|
146
|
+
socket = TCPSocket.new('localhost', @intranet.port)
|
|
147
|
+
socket.puts("GET /responder/index.html HTTP/1.1\r\n" \
|
|
148
|
+
"Host: localhost:#{@intranet.port}\r\n\r\n")
|
|
149
|
+
expect(socket.gets).to include('HTTP/1.1 200 OK')
|
|
150
|
+
socket.close
|
|
174
151
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
socket = TCPSocket.new('localhost', @intranet.port)
|
|
181
|
-
socket.puts("GET /design/home/style.css HTTP/1.1\r\n" \
|
|
182
|
-
"Host: localhost:#{@intranet.port}\r\n\r\n")
|
|
183
|
-
expect(socket.gets).to include('HTTP/1.1 200 OK')
|
|
184
|
-
socket.close
|
|
152
|
+
socket = TCPSocket.new('localhost', @intranet.port)
|
|
153
|
+
socket.puts("GET /resp/onder/index.html HTTP/1.1\r\n" \
|
|
154
|
+
"Host: localhost:#{@intranet.port}\r\n\r\n")
|
|
155
|
+
expect(socket.gets).to include('HTTP/1.1 200 OK')
|
|
156
|
+
socket.close
|
|
185
157
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
socket.puts("GET /design/responder/style.css HTTP/1.1\r\n" \
|
|
193
|
-
"Host: localhost:#{@intranet.port}\r\n\r\n")
|
|
194
|
-
expect(socket.gets).to include('HTTP/1.1 200 OK')
|
|
195
|
-
socket.close
|
|
158
|
+
socket = TCPSocket.new('localhost', @intranet.port)
|
|
159
|
+
socket.puts("GET /resp/onder/index2.html HTTP/1.1\r\n" \
|
|
160
|
+
"Host: localhost:#{@intranet.port}\r\n\r\n")
|
|
161
|
+
expect(socket.gets).to include('HTTP/1.1 404 Not Found')
|
|
162
|
+
socket.close
|
|
163
|
+
end
|
|
196
164
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
thread.join
|
|
210
|
-
end
|
|
165
|
+
it 'should have its www/ directory available under the subfolder design/' do
|
|
166
|
+
socket = TCPSocket.new('localhost', @intranet.port)
|
|
167
|
+
socket.puts("GET /responder/design/style.css HTTP/1.1\r\n" \
|
|
168
|
+
"Host: localhost:#{@intranet.port}\r\n\r\n")
|
|
169
|
+
expect(socket.gets).to include('HTTP/1.1 200 OK')
|
|
170
|
+
socket.close
|
|
171
|
+
|
|
172
|
+
socket = TCPSocket.new('localhost', @intranet.port)
|
|
173
|
+
socket.puts("GET /resp/onder/design/style.css HTTP/1.1\r\n" \
|
|
174
|
+
"Host: localhost:#{@intranet.port}\r\n\r\n")
|
|
175
|
+
expect(socket.gets).to include('HTTP/1.1 200 OK')
|
|
176
|
+
socket.close
|
|
211
177
|
end
|
|
212
178
|
end
|
|
213
179
|
|
|
214
180
|
context 'given a valid and registered module' do
|
|
215
|
-
it 'should be called with the URL path and query' do
|
|
181
|
+
it 'should be called with the decoded URL path and query in UTF-8 encoding' do
|
|
216
182
|
begin
|
|
217
183
|
@intranet = described_class.new(Intranet::Logger.new(Intranet::Logger::FATAL))
|
|
218
184
|
responder = Intranet::TestResponder.new('/index.html' => [200, 'text/html', ''])
|
|
219
|
-
@intranet.register_module(responder, [], responder.resources_dir)
|
|
185
|
+
@intranet.register_module(responder, ['responder'], responder.resources_dir)
|
|
220
186
|
thread = Thread.new do
|
|
221
187
|
@intranet.start
|
|
222
188
|
end
|
|
@@ -224,25 +190,17 @@ RSpec.describe Intranet::Core do
|
|
|
224
190
|
end
|
|
225
191
|
|
|
226
192
|
socket = TCPSocket.new('localhost', @intranet.port)
|
|
227
|
-
socket.puts("GET /query?var1=value1&var2=value2 HTTP/1.1\r\n" \
|
|
228
|
-
"Host: localhost:#{@intranet.port}\r\n\r\n")
|
|
229
|
-
expect(socket.gets).to include('HTTP/1.1 200 OK')
|
|
230
|
-
while (line = socket.gets.chomp) # consume HTTP response headers
|
|
231
|
-
break if line.empty?
|
|
232
|
-
end
|
|
233
|
-
line = socket.gets.chomp
|
|
234
|
-
expect(line).to eql({ 'var1' => 'value1', 'var2' => 'value2' }.to_s)
|
|
235
|
-
socket.close
|
|
236
|
-
|
|
237
|
-
socket = TCPSocket.new('localhost', @intranet.port)
|
|
238
|
-
socket.puts("GET /query?foo=bar&baz=boz HTTP/1.1\r\n" \
|
|
193
|
+
socket.puts("GET /responder/query%20t?var1=value1&var2=value2 HTTP/1.1\r\n" \
|
|
239
194
|
"Host: localhost:#{@intranet.port}\r\n\r\n")
|
|
240
195
|
expect(socket.gets).to include('HTTP/1.1 200 OK')
|
|
241
196
|
while (line = socket.gets.chomp) # consume HTTP response headers
|
|
242
197
|
break if line.empty?
|
|
243
198
|
end
|
|
244
199
|
line = socket.gets.chomp
|
|
245
|
-
expect(line).to eql(
|
|
200
|
+
expect(line).to eql(
|
|
201
|
+
'PATH=/query t (UTF-8), ' \
|
|
202
|
+
'QUERY={var1 (UTF-8) => value1 (UTF-8),var2 (UTF-8) => value2 (UTF-8)}'
|
|
203
|
+
)
|
|
246
204
|
ensure
|
|
247
205
|
socket.close
|
|
248
206
|
Thread.kill(thread)
|
|
@@ -262,7 +220,7 @@ RSpec.describe Intranet::Core do
|
|
|
262
220
|
['/responder.css', 'nav.css'],
|
|
263
221
|
['module.js', '/js/interactive.js']
|
|
264
222
|
)
|
|
265
|
-
@intranet.register_module(responder, [], responder.resources_dir)
|
|
223
|
+
@intranet.register_module(responder, ['r'], responder.resources_dir)
|
|
266
224
|
thread = Thread.new do
|
|
267
225
|
@intranet.start
|
|
268
226
|
end
|
|
@@ -270,7 +228,7 @@ RSpec.describe Intranet::Core do
|
|
|
270
228
|
end
|
|
271
229
|
|
|
272
230
|
socket = TCPSocket.new('localhost', @intranet.port)
|
|
273
|
-
socket.puts("GET /index.html HTTP/1.1\r\nHost: localhost:#{@intranet.port}\r\n\r\n")
|
|
231
|
+
socket.puts("GET /r/index.html HTTP/1.1\r\nHost: localhost:#{@intranet.port}\r\n\r\n")
|
|
274
232
|
|
|
275
233
|
# Return code: HTTP error 200
|
|
276
234
|
expect(socket.gets).to include('HTTP/1.1 200 OK')
|
|
@@ -290,17 +248,17 @@ RSpec.describe Intranet::Core do
|
|
|
290
248
|
expect(html).to match(%r{<body>.*<h1>.*#{hostname.capitalize}.*</h1>.*</body>}m)
|
|
291
249
|
expect(html).to match(%r{<footer>.*#{hostname}.*</footer>}m)
|
|
292
250
|
|
|
293
|
-
# Returned HTML document: includes all CSS dependencies
|
|
251
|
+
# Returned HTML document: includes all CSS dependencies, relative or absolute path
|
|
294
252
|
expect(html).to match(%r{<link href='/responder.css' rel='stylesheet' type='text/css'})
|
|
295
|
-
expect(html).to match(%r{<link href='nav.css' rel='stylesheet' type='text/css'})
|
|
253
|
+
expect(html).to match(%r{<link href='/r/nav.css' rel='stylesheet' type='text/css'})
|
|
296
254
|
|
|
297
255
|
# Returned HTML document: includes all JS dependencies
|
|
298
|
-
expect(html).to match(%r{<script defer='defer' src='module.js'></script>})
|
|
256
|
+
expect(html).to match(%r{<script defer='defer' src='/r/module.js'></script>})
|
|
299
257
|
expect(html).to match(%r{<script defer='defer' src='/js/interactive.js'></script>})
|
|
300
258
|
|
|
301
259
|
# Returned HTML document: includes Intranet Core name, version and URL
|
|
302
260
|
expect(html).to match(
|
|
303
|
-
%r{<footer>.*<a href='#{Intranet::Core::HOMEPAGE_URL}'.*>#{Intranet::Core::NAME}</a>.*#{Intranet::Core::VERSION}.*</footer>}m # rubocop:disable
|
|
261
|
+
%r{<footer>.*<a href='#{Intranet::Core::HOMEPAGE_URL}'.*>#{Intranet::Core::NAME}</a>.*#{Intranet::Core::VERSION}.*</footer>}m # rubocop:disable Layout/LineLength
|
|
304
262
|
)
|
|
305
263
|
|
|
306
264
|
# Returned HTML document: includes all registered modules version name, version and URL
|
|
@@ -320,7 +278,7 @@ RSpec.describe Intranet::Core do
|
|
|
320
278
|
'/index.html' => [206, 'text/html', { content: 'PARTIAL_CONTENT', title: 'MyTitle' }]
|
|
321
279
|
)
|
|
322
280
|
other_responder = Intranet::TestResponder.new({}, [], [], true)
|
|
323
|
-
@intranet.register_module(responder, [], responder.resources_dir)
|
|
281
|
+
@intranet.register_module(responder, %w[r], responder.resources_dir)
|
|
324
282
|
@intranet.register_module(responder, %w[dep_th1], responder.resources_dir)
|
|
325
283
|
@intranet.register_module(responder, %w[depth2 res_p1], responder.resources_dir)
|
|
326
284
|
@intranet.register_module(responder, %w[depth2 resp2], responder.resources_dir)
|
|
@@ -335,7 +293,7 @@ RSpec.describe Intranet::Core do
|
|
|
335
293
|
end
|
|
336
294
|
|
|
337
295
|
socket = TCPSocket.new('localhost', @intranet.port)
|
|
338
|
-
socket.puts("GET /index.html HTTP/1.1\r\nHost: localhost:#{@intranet.port}\r\n\r\n")
|
|
296
|
+
socket.puts("GET /r/index.html HTTP/1.1\r\nHost: localhost:#{@intranet.port}\r\n\r\n")
|
|
339
297
|
|
|
340
298
|
# Return code: HTTP error 200
|
|
341
299
|
expect(socket.gets).to include('HTTP/1.1 200 OK')
|
|
@@ -367,12 +325,47 @@ RSpec.describe Intranet::Core do
|
|
|
367
325
|
end
|
|
368
326
|
end
|
|
369
327
|
|
|
328
|
+
describe '#home_url=' do
|
|
329
|
+
context 'given a relative URL' do
|
|
330
|
+
it 'should fail' do
|
|
331
|
+
@intranet = described_class.new(Intranet::Logger.new(Intranet::Logger::FATAL))
|
|
332
|
+
expect { @intranet.home_url = 'foo/index.html' }.to raise_error ArgumentError
|
|
333
|
+
end
|
|
334
|
+
end
|
|
335
|
+
|
|
336
|
+
context 'given an absolute URL' do
|
|
337
|
+
it 'should set up a redirection from /index.html to the provided URL' do
|
|
338
|
+
begin
|
|
339
|
+
@intranet = described_class.new(Intranet::Logger.new(Intranet::Logger::FATAL))
|
|
340
|
+
@intranet.home_url = '/responder/index.html'
|
|
341
|
+
thread = Thread.new do
|
|
342
|
+
@intranet.start
|
|
343
|
+
end
|
|
344
|
+
while @intranet.instance_variable_get(:@server).status != :Running
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
socket = TCPSocket.new('localhost', @intranet.port)
|
|
348
|
+
socket.puts("GET /index.html HTTP/1.1\r\nHost: localhost:#{@intranet.port}\r\n\r\n")
|
|
349
|
+
expect(socket.gets).to include('HTTP/1.1 307 Temporary Redirect')
|
|
350
|
+
while (line = socket.gets.chomp) # search the HTTP response for the 'Location' header
|
|
351
|
+
break if line.start_with?('Location:')
|
|
352
|
+
end
|
|
353
|
+
expect(line).to include("http://localhost:#{@intranet.port}/responder/index.html")
|
|
354
|
+
ensure
|
|
355
|
+
socket.close
|
|
356
|
+
Thread.kill(thread)
|
|
357
|
+
thread.join
|
|
358
|
+
end
|
|
359
|
+
end
|
|
360
|
+
end
|
|
361
|
+
end
|
|
362
|
+
|
|
370
363
|
describe '#stop' do
|
|
371
364
|
it 'should stop the web server and finalize all registered responders' do
|
|
372
365
|
begin
|
|
373
366
|
@intranet = described_class.new(Intranet::Logger.new(Intranet::Logger::FATAL))
|
|
374
367
|
responder = Intranet::TestResponder.new('/index.html' => [200, 'text/html', 'CONTENT'])
|
|
375
|
-
@intranet.register_module(responder, [], responder.resources_dir)
|
|
368
|
+
@intranet.register_module(responder, %w[r], responder.resources_dir)
|
|
376
369
|
thread = Thread.new do
|
|
377
370
|
@intranet.start
|
|
378
371
|
end
|
|
@@ -380,7 +373,7 @@ RSpec.describe Intranet::Core do
|
|
|
380
373
|
end
|
|
381
374
|
|
|
382
375
|
socket = TCPSocket.new('localhost', @intranet.port)
|
|
383
|
-
socket.puts("GET /index.html HTTP/1.1\r\nHost: localhost:#{@intranet.port}\r\n\r\n")
|
|
376
|
+
socket.puts("GET /r/index.html HTTP/1.1\r\nHost: localhost:#{@intranet.port}\r\n\r\n")
|
|
384
377
|
expect(socket.gets).to include('HTTP/1.1 200 OK')
|
|
385
378
|
expect(responder.finalized).to be false
|
|
386
379
|
socket.close
|
|
@@ -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: 1.
|
|
4
|
+
version: 2.1.4
|
|
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: 2020-06-12 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
|
|
@@ -189,7 +189,6 @@ files:
|
|
|
189
189
|
- spec/intranet/logger_spec.rb
|
|
190
190
|
- spec/spec_helper.rb
|
|
191
191
|
- spec/test_responder/responder.rb
|
|
192
|
-
- spec/test_responder/www/style.css
|
|
193
192
|
homepage: https://rubygems.org/gems/intranet-core
|
|
194
193
|
licenses:
|
|
195
194
|
- MIT
|
|
@@ -211,7 +210,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
211
210
|
version: '0'
|
|
212
211
|
requirements: []
|
|
213
212
|
rubyforge_project:
|
|
214
|
-
rubygems_version: 2.
|
|
213
|
+
rubygems_version: 2.7.6.2
|
|
215
214
|
signing_key:
|
|
216
215
|
specification_version: 4
|
|
217
216
|
summary: Core component to build a custom intranet.
|