intranet-core 1.2.0 → 2.1.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: b297a91ba8ca9fc6a44723c4f9f738d879193828
4
- data.tar.gz: bc44f0b1858f045978da8d39824ad36a0a52c485
2
+ SHA256:
3
+ metadata.gz: e1d7d91fa0a962e4835681a46f50c7e49b5aaa77327ab112d2999aa90b26a312
4
+ data.tar.gz: 3c67a863896c42b5b83de33863e372678bae92ee831c05f91a6484343ecff88e
5
5
  SHA512:
6
- metadata.gz: a5094ed435333e80537e85603359276059b345554ae185cdb6c56e9d843ca385b31fc1e1e008e329114c3f5584e509d89087dab4e3e24423a2fbb35f0081457b
7
- data.tar.gz: e06fea82c25af97e56230a51df3f4b4f9447fd2218d9ca7b28b0663f83d43d3713cda474aab06c15c45a5860680d02edf5adf95742ee552331e19755e01a19ad
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 URL path from server root
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 URL path from server root
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
@@ -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. If +path+ is empty, the
44
- # module will be used as Home module. Otherwise, the module will be accessible through the
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. If empty, the responder will be registered as Home responder
49
- # (to serve /index.html in particular). Subdirectories are allowed using
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
- # @raise [ArgumentError] If one of the element of the +path+ contains invalid characters.
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('/design/' + path.join('/'),
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, responder_path = get_responder(path)
51
- status, mime_type, body = responder.generate_page(responder_path, query)
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. If empty, the responder will be registered as Home responder
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 one of the element of the +path+ contains invalid characters.
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 path.all?(&:urlized?)
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 [Array] The responder instance (possibly nil) and the remaining of the URL path that
86
- # has not been parsed.
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
- relative_path = path[1..-1].split('/').delete_if do |part|
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, '/' + relative_path.join('/')]
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.past which has been
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[-1] == '/' # path is normalized (ie. does not contain '../')
29
+ path = request.path.force_encoding('UTF-8')
30
+ path += 'index.html' if path.end_with?('/')
31
31
 
32
- status, content_type, body = @builder.do_get(path, request.query)
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
@@ -6,7 +6,7 @@ module Intranet
6
6
  NAME = 'intranet-core'
7
7
 
8
8
  # The version of the gem, according to semantic versionning.
9
- VERSION = '1.2.0'
9
+ VERSION = '2.1.4'
10
10
 
11
11
  # The URL of the gem homepage.
12
12
  HOMEPAGE_URL = 'https://rubygems.org/gems/intranet-core'
@@ -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
- %link{rel: 'stylesheet', type: 'text/css', href: url}
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
- %script{src: url, defer: 'defer'}
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();'}= '&#9776;'
@@ -10,7 +10,8 @@ function openNavMenu() {
10
10
  }
11
11
 
12
12
  function closeNavMenu() {
13
- document.querySelectorAll('header nav')[0].style.width = '0';
13
+ /* Remove value property set in openNavMenu() */
14
+ document.querySelectorAll('header nav')[0].style.width = '';
14
15
  }
15
16
 
16
17
  function openModal() {
@@ -45,7 +45,6 @@ header {
45
45
  top: 0px;
46
46
  width: 100%;
47
47
  margin: auto;
48
- height: 57px;
49
48
  background-color: #1e262b;
50
49
  color: white;
51
50
  z-index: 2;
@@ -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(nil, [], '') }.to raise_error Errno::EALREADY
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
- expect { @intranet.register_module(nil, ['Invalid'], '') }.to raise_error ArgumentError
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 with no path (home module)' do
119
- it 'should be used to serve all URI' do
120
- begin
121
- @intranet = described_class.new(Intranet::Logger.new(Intranet::Logger::FATAL))
122
- responder = Intranet::TestResponder.new('/index.html' => [200, 'text/html', ''],
123
- '/folder/index.html' => [200, 'text/html', ''])
124
- @intranet.register_module(responder, [], responder.resources_dir)
125
- thread = Thread.new do
126
- @intranet.start
127
- end
128
- while @intranet.instance_variable_get(:@server).status != :Running
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
- context 'when a valid module is registered with a path' do
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
- begin
164
- @intranet = described_class.new(Intranet::Logger.new(Intranet::Logger::FATAL))
165
- responder = Intranet::TestResponder.new('/index.html' => [200, 'text/html', ''])
166
- @intranet.register_module(responder, [], responder.resources_dir)
167
- @intranet.register_module(responder, %w[responder], responder.resources_dir)
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
- socket = TCPSocket.new('localhost', @intranet.port)
176
- socket.puts("GET /index.html HTTP/1.1\r\n" \
177
- "Host: localhost:#{@intranet.port}\r\n\r\n")
178
- expect(socket.gets).to include('HTTP/1.1 200 OK')
179
- socket.close
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
- socket = TCPSocket.new('localhost', @intranet.port)
187
- socket.puts("GET /responder/index.html HTTP/1.1\r\n" \
188
- "Host: localhost:#{@intranet.port}\r\n\r\n")
189
- expect(socket.gets).to include('HTTP/1.1 200 OK')
190
- socket.close
191
- socket = TCPSocket.new('localhost', @intranet.port)
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
- socket = TCPSocket.new('localhost', @intranet.port)
198
- socket.puts("GET /resp/onder/index.html HTTP/1.1\r\n" \
199
- "Host: localhost:#{@intranet.port}\r\n\r\n")
200
- expect(socket.gets).to include('HTTP/1.1 200 OK')
201
- socket.close
202
- socket = TCPSocket.new('localhost', @intranet.port)
203
- socket.puts("GET /design/resp/onder/style.css 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
- ensure
207
- socket.close
208
- Thread.kill(thread)
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({ 'foo' => 'bar', 'baz' => 'boz' }.to_s)
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 Metrics/LineLength
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
- __dir__
41
+ super
42
42
  end
43
43
 
44
44
  def generate_page(path, query)
45
- if path == '/query'
46
- [200, 'text/plain', query.to_s + "\r\n"]
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.2.0
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: 2019-02-03 00:00:00.000000000 Z
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.63.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.63.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.5.2.1
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.
@@ -1,5 +0,0 @@
1
- /**
2
- * test_responder/www/style.css
3
- * Design for the Test Responder of the Intranet-Core.
4
- */
5
-