Merge pull request #1528 from maxmeyer/feature/listener
Support Bind to address for middleman
This commit is contained in:
commit
d2f8dc9932
|
@ -14,6 +14,7 @@ AllCops:
|
||||||
- 'middleman-core/fixtures/**/*'
|
- 'middleman-core/fixtures/**/*'
|
||||||
- 'middleman-core/features/**/*'
|
- 'middleman-core/features/**/*'
|
||||||
- 'middleman-core/spec/**/*'
|
- 'middleman-core/spec/**/*'
|
||||||
|
DisplayCopNames: true
|
||||||
LineLength:
|
LineLength:
|
||||||
Enabled: false
|
Enabled: false
|
||||||
MethodLength:
|
MethodLength:
|
||||||
|
|
5
Gemfile
5
Gemfile
|
@ -9,7 +9,7 @@ gem 'pry', '~> 0.10', group: :development
|
||||||
gem 'aruba', '~> 0.7.4'
|
gem 'aruba', '~> 0.7.4'
|
||||||
gem 'rspec', '~> 3.0'
|
gem 'rspec', '~> 3.0'
|
||||||
gem 'fivemat', '~> 1.3'
|
gem 'fivemat', '~> 1.3'
|
||||||
gem 'cucumber', '~> 1.3'
|
gem 'cucumber', '~> 2.0'
|
||||||
|
|
||||||
# Optional middleman dependencies, included for tests
|
# Optional middleman dependencies, included for tests
|
||||||
gem 'less', '2.3', require: false
|
gem 'less', '2.3', require: false
|
||||||
|
@ -20,6 +20,9 @@ gem 'sinatra', '>= 1.4', require: false
|
||||||
gem 'redcarpet', '>= 3.1', require: false unless RUBY_ENGINE == 'jruby'
|
gem 'redcarpet', '>= 3.1', require: false unless RUBY_ENGINE == 'jruby'
|
||||||
gem 'asciidoctor', '~> 0.1', require: false
|
gem 'asciidoctor', '~> 0.1', require: false
|
||||||
|
|
||||||
|
# Dns server to test preview server
|
||||||
|
gem 'rubydns', '~> 1.0.1', require: false
|
||||||
|
|
||||||
# To test javascript
|
# To test javascript
|
||||||
gem 'poltergeist', '~> 1.6.0', require: false
|
gem 'poltergeist', '~> 1.6.0', require: false
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@ Cucumber::Rake::Task.new do |t|
|
||||||
exempt_tags << '--tags ~@encoding' unless Object.const_defined?(:Encoding)
|
exempt_tags << '--tags ~@encoding' unless Object.const_defined?(:Encoding)
|
||||||
exempt_tags << '--tags ~@nowindows' if Gem.win_platform?
|
exempt_tags << '--tags ~@nowindows' if Gem.win_platform?
|
||||||
exempt_tags << '--tags ~@travishatesme' if ENV['TRAVIS'] == 'true'
|
exempt_tags << '--tags ~@travishatesme' if ENV['TRAVIS'] == 'true'
|
||||||
t.cucumber_opts = "--color #{exempt_tags.join(' ')} --strict --format #{ENV['CUCUMBER_FORMAT'] || 'Fivemat'}"
|
t.cucumber_opts = "--require features --color #{exempt_tags.join(' ')} --strict --format #{ENV['CUCUMBER_FORMAT'] || 'Fivemat'}"
|
||||||
end
|
end
|
||||||
|
|
||||||
Cucumber::Rake::Task.new(:cucumber_wip) do |t|
|
Cucumber::Rake::Task.new(:cucumber_wip) do |t|
|
||||||
|
|
1
middleman-core/.rspec
Normal file
1
middleman-core/.rspec
Normal file
|
@ -0,0 +1 @@
|
||||||
|
--color
|
2
middleman-core/cucumber.yml
Normal file
2
middleman-core/cucumber.yml
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
default: --require features --tags ~@wip
|
||||||
|
wip: --require features --tags @wip
|
532
middleman-core/features/cli/preview_server.feature
Normal file
532
middleman-core/features/cli/preview_server.feature
Normal file
|
@ -0,0 +1,532 @@
|
||||||
|
Feature: Run the preview server
|
||||||
|
|
||||||
|
As a software developer
|
||||||
|
I want to start the preview server
|
||||||
|
In order to view my changes immediately in the browser
|
||||||
|
|
||||||
|
Background:
|
||||||
|
Given a fixture app "preview-server-app"
|
||||||
|
And the default aruba timeout is 30 seconds
|
||||||
|
|
||||||
|
Scenario: Start the server with defaults
|
||||||
|
When I run `middleman server` interactively
|
||||||
|
And I stop middleman if the output contains:
|
||||||
|
"""
|
||||||
|
Inspect your site configuration
|
||||||
|
"""
|
||||||
|
And the output should contain:
|
||||||
|
"""
|
||||||
|
View your site at "http://
|
||||||
|
"""
|
||||||
|
And the output should contain:
|
||||||
|
"""
|
||||||
|
Inspect your site configuration at "http://
|
||||||
|
"""
|
||||||
|
|
||||||
|
Scenario: Start the server with defaults in verbose mode
|
||||||
|
When I run `middleman server --verbose` interactively
|
||||||
|
And I stop middleman if the output contains:
|
||||||
|
"""
|
||||||
|
Inspect your site configuration
|
||||||
|
"""
|
||||||
|
Then the output should contain:
|
||||||
|
"""
|
||||||
|
The Middleman preview server is bind to ":::4567", "0.0.0.0:4567"
|
||||||
|
"""
|
||||||
|
And the output should contain:
|
||||||
|
"""
|
||||||
|
View your site at "http://
|
||||||
|
"""
|
||||||
|
And the output should contain:
|
||||||
|
"""
|
||||||
|
Inspect your site configuration at "http://
|
||||||
|
"""
|
||||||
|
|
||||||
|
@ruby-2.1
|
||||||
|
Scenario: Start the server with defaults in verbose mode, when a local mdns server resolves the local hostname
|
||||||
|
Given I start a mdns server for the local hostname
|
||||||
|
When I run `middleman server --verbose` interactively
|
||||||
|
And I stop middleman if the output contains:
|
||||||
|
"""
|
||||||
|
Inspect your site configuration
|
||||||
|
"""
|
||||||
|
Then the output should contain:
|
||||||
|
"""
|
||||||
|
The Middleman preview server is bind to ":::4567", "0.0.0.0:4567"
|
||||||
|
"""
|
||||||
|
And the output should contain:
|
||||||
|
"""
|
||||||
|
View your site at "http://
|
||||||
|
"""
|
||||||
|
And the output should contain:
|
||||||
|
"""
|
||||||
|
Inspect your site configuration at "http://
|
||||||
|
"""
|
||||||
|
|
||||||
|
Scenario: Start the server with bind address 127.0.0.1
|
||||||
|
Given I have a local hosts file with:
|
||||||
|
"""
|
||||||
|
# <ip-address> <hostname.domain.org> <hostname>
|
||||||
|
127.0.0.1 localhost.localdomain localhost
|
||||||
|
"""
|
||||||
|
When I run `middleman server --verbose --bind-address 127.0.0.1` interactively
|
||||||
|
And I stop middleman if the output contains:
|
||||||
|
"""
|
||||||
|
Inspect your site configuration
|
||||||
|
"""
|
||||||
|
Then the output should contain:
|
||||||
|
"""
|
||||||
|
The Middleman preview server is bind to "127.0.0.1:4567"
|
||||||
|
"""
|
||||||
|
And the output should contain:
|
||||||
|
"""
|
||||||
|
View your site at "http://127.0.0.1:4567"
|
||||||
|
"""
|
||||||
|
And the output should contain:
|
||||||
|
"""
|
||||||
|
Inspect your site configuration at "http://127.0.0.1:4567/__middleman"
|
||||||
|
"""
|
||||||
|
|
||||||
|
Scenario: Start the server with bind address 127.0.0.1 configured via config.rb
|
||||||
|
Given I have a local hosts file with:
|
||||||
|
"""
|
||||||
|
# <ip-address> <hostname.domain.org> <hostname>
|
||||||
|
127.0.0.1 localhost.localdomain localhost
|
||||||
|
"""
|
||||||
|
And a file named "config.rb" with:
|
||||||
|
"""
|
||||||
|
set :bind_address, '127.0.0.1'
|
||||||
|
"""
|
||||||
|
When I run `middleman server --verbose` interactively
|
||||||
|
And I stop middleman if the output contains:
|
||||||
|
"""
|
||||||
|
Inspect your site configuration
|
||||||
|
"""
|
||||||
|
Then the output should contain:
|
||||||
|
"""
|
||||||
|
The Middleman preview server is bind to "127.0.0.1:4567"
|
||||||
|
"""
|
||||||
|
And the output should contain:
|
||||||
|
"""
|
||||||
|
View your site at "http://127.0.0.1:4567"
|
||||||
|
"""
|
||||||
|
And the output should contain:
|
||||||
|
"""
|
||||||
|
Inspect your site configuration at "http://127.0.0.1:4567/__middleman"
|
||||||
|
"""
|
||||||
|
|
||||||
|
Scenario: Start the server with bind address 127.0.0.5
|
||||||
|
|
||||||
|
This will have no hostname attached because the hosts file, the DNS server
|
||||||
|
and the MDNS-server do not know anything about 127.0.0.5
|
||||||
|
|
||||||
|
When I run `middleman server --verbose --bind-address 127.0.0.5` interactively
|
||||||
|
And I stop middleman if the output contains:
|
||||||
|
"""
|
||||||
|
Inspect your site configuration
|
||||||
|
"""
|
||||||
|
Then the output should contain:
|
||||||
|
"""
|
||||||
|
The Middleman preview server is bind to "127.0.0.5:4567"
|
||||||
|
"""
|
||||||
|
And the output should contain:
|
||||||
|
"""
|
||||||
|
View your site at "http://127.0.0.5:4567"
|
||||||
|
"""
|
||||||
|
And the output should contain:
|
||||||
|
"""
|
||||||
|
Inspect your site configuration at "http://127.0.0.5:4567/__middleman"
|
||||||
|
"""
|
||||||
|
|
||||||
|
Scenario: Start the server with bind address ::1
|
||||||
|
Given a file named ".hosts" with:
|
||||||
|
"""
|
||||||
|
# <ip-address> <hostname.domain.org> <hostname>
|
||||||
|
::1 localhost.localdomain localhost
|
||||||
|
"""
|
||||||
|
When I run `middleman server --verbose --bind-address ::1` interactively
|
||||||
|
And I stop middleman if the output contains:
|
||||||
|
"""
|
||||||
|
Inspect your site configuration
|
||||||
|
"""
|
||||||
|
Then the output should contain:
|
||||||
|
"""
|
||||||
|
The Middleman preview server is bind to "::1:4567"
|
||||||
|
"""
|
||||||
|
And the output should contain:
|
||||||
|
"""
|
||||||
|
View your site at "http://[::1]:4567"
|
||||||
|
"""
|
||||||
|
And the output should contain:
|
||||||
|
"""
|
||||||
|
Inspect your site configuration at "http://[::1]:4567/__middleman"
|
||||||
|
"""
|
||||||
|
|
||||||
|
Scenario: Start the server with bind address 0.0.0.0
|
||||||
|
When I run `middleman server --verbose --bind-address 0.0.0.0` interactively
|
||||||
|
And I stop middleman if the output contains:
|
||||||
|
"""
|
||||||
|
Inspect your site configuration
|
||||||
|
"""
|
||||||
|
Then the output should contain:
|
||||||
|
"""
|
||||||
|
The Middleman preview server is bind to "0.0.0.0:4567"
|
||||||
|
"""
|
||||||
|
And the output should contain:
|
||||||
|
"""
|
||||||
|
View your site at "http://
|
||||||
|
"""
|
||||||
|
And the output should contain:
|
||||||
|
"""
|
||||||
|
Inspect your site configuration at "http://
|
||||||
|
"""
|
||||||
|
|
||||||
|
Scenario: Start the server with bind address ::
|
||||||
|
When I run `middleman server --verbose --bind-address ::` interactively
|
||||||
|
And I stop middleman if the output contains:
|
||||||
|
"""
|
||||||
|
Inspect your site configuration
|
||||||
|
"""
|
||||||
|
Then the output should contain:
|
||||||
|
"""
|
||||||
|
The Middleman preview server is bind to ":::4567"
|
||||||
|
"""
|
||||||
|
And the output should contain:
|
||||||
|
"""
|
||||||
|
View your site at "http://
|
||||||
|
"""
|
||||||
|
And the output should contain:
|
||||||
|
"""
|
||||||
|
Inspect your site configuration at "http://
|
||||||
|
"""
|
||||||
|
|
||||||
|
Scenario: Start the server with server name "localhost"
|
||||||
|
Given I have a local hosts file with:
|
||||||
|
"""
|
||||||
|
# <ip-address> <hostname.domain.org> <hostname>
|
||||||
|
127.0.0.1 localhost.localdomain localhost
|
||||||
|
"""
|
||||||
|
When I run `middleman server --verbose --server-name localhost` interactively
|
||||||
|
And I stop middleman if the output contains:
|
||||||
|
"""
|
||||||
|
Inspect your site configuration
|
||||||
|
"""
|
||||||
|
Then the output should contain:
|
||||||
|
"""
|
||||||
|
The Middleman preview server is bind to "127.0.0.1:4567"
|
||||||
|
"""
|
||||||
|
And the output should contain:
|
||||||
|
"""
|
||||||
|
View your site at "http://localhost:4567", "http://127.0.0.1:4567"
|
||||||
|
"""
|
||||||
|
And the output should contain:
|
||||||
|
"""
|
||||||
|
Inspect your site configuration at "http://localhost:4567/__middleman", "http://127.0.0.1:4567/__middleman"
|
||||||
|
"""
|
||||||
|
|
||||||
|
Scenario: Start the server with server name "localhost" configured via config.rb
|
||||||
|
Given I have a local hosts file with:
|
||||||
|
"""
|
||||||
|
# <ip-address> <hostname.domain.org> <hostname>
|
||||||
|
127.0.0.1 localhost.localdomain localhost
|
||||||
|
"""
|
||||||
|
And a file named "config.rb" with:
|
||||||
|
"""
|
||||||
|
set :server_name, 'localhost'
|
||||||
|
"""
|
||||||
|
When I run `middleman server --verbose` interactively
|
||||||
|
And I stop middleman if the output contains:
|
||||||
|
"""
|
||||||
|
Inspect your site configuration
|
||||||
|
"""
|
||||||
|
Then the output should contain:
|
||||||
|
"""
|
||||||
|
The Middleman preview server is bind to "127.0.0.1:4567"
|
||||||
|
"""
|
||||||
|
And the output should contain:
|
||||||
|
"""
|
||||||
|
View your site at "http://localhost:4567", "http://127.0.0.1:4567"
|
||||||
|
"""
|
||||||
|
And the output should contain:
|
||||||
|
"""
|
||||||
|
Inspect your site configuration at "http://localhost:4567/__middleman", "http://127.0.0.1:4567/__middleman"
|
||||||
|
"""
|
||||||
|
|
||||||
|
Scenario: Start the server with server name "localhost" and bind address "127.0.0.1"
|
||||||
|
Given I have a local hosts file with:
|
||||||
|
"""
|
||||||
|
# <ip-address> <hostname.domain.org> <hostname>
|
||||||
|
127.0.0.1 localhost.localdomain localhost
|
||||||
|
"""
|
||||||
|
When I run `middleman server --verbose --server-name localhost --bind-address 127.0.0.1` interactively
|
||||||
|
And I stop middleman if the output contains:
|
||||||
|
"""
|
||||||
|
Inspect your site configuration
|
||||||
|
"""
|
||||||
|
Then the output should contain:
|
||||||
|
"""
|
||||||
|
The Middleman preview server is bind to "127.0.0.1:4567"
|
||||||
|
"""
|
||||||
|
And the output should contain:
|
||||||
|
"""
|
||||||
|
View your site at "http://localhost:4567", "http://127.0.0.1:4567"
|
||||||
|
"""
|
||||||
|
And the output should contain:
|
||||||
|
"""
|
||||||
|
Inspect your site configuration at "http://localhost:4567/__middleman", "http://127.0.0.1:4567/__middleman"
|
||||||
|
"""
|
||||||
|
|
||||||
|
Scenario: Start the server with server name "127.0.0.1"
|
||||||
|
When I run `middleman server --verbose --server-name 127.0.0.1` interactively
|
||||||
|
And I stop middleman if the output contains:
|
||||||
|
"""
|
||||||
|
Inspect your site configuration
|
||||||
|
"""
|
||||||
|
Then the output should contain:
|
||||||
|
"""
|
||||||
|
The Middleman preview server is bind to "127.0.0.1:4567"
|
||||||
|
"""
|
||||||
|
And the output should contain:
|
||||||
|
"""
|
||||||
|
View your site at "http://127.0.0.1:4567"
|
||||||
|
"""
|
||||||
|
And the output should contain:
|
||||||
|
"""
|
||||||
|
Inspect your site configuration at "http://127.0.0.1:4567/__middleman"
|
||||||
|
"""
|
||||||
|
|
||||||
|
Scenario: Start the server with server name "::1"
|
||||||
|
When I run `middleman server --verbose --server-name ::1` interactively
|
||||||
|
And I stop middleman if the output contains:
|
||||||
|
"""
|
||||||
|
Inspect your site configuration
|
||||||
|
"""
|
||||||
|
Then the output should contain:
|
||||||
|
"""
|
||||||
|
The Middleman preview server is bind to "::1:4567"
|
||||||
|
"""
|
||||||
|
And the output should contain:
|
||||||
|
"""
|
||||||
|
View your site at "http://[::1]:4567"
|
||||||
|
"""
|
||||||
|
And the output should contain:
|
||||||
|
"""
|
||||||
|
Inspect your site configuration at "http://[::1]:4567/__middleman"
|
||||||
|
"""
|
||||||
|
|
||||||
|
Scenario: Start the server with https
|
||||||
|
When I run `middleman server --verbose --https` interactively
|
||||||
|
And I stop middleman if the output contains:
|
||||||
|
"""
|
||||||
|
Inspect your site configuration
|
||||||
|
"""
|
||||||
|
Then the output should contain:
|
||||||
|
"""
|
||||||
|
The Middleman preview server is bind to ":::4567", "0.0.0.0:4567"
|
||||||
|
"""
|
||||||
|
And the output should contain:
|
||||||
|
"""
|
||||||
|
View your site at "https://
|
||||||
|
"""
|
||||||
|
And the output should contain:
|
||||||
|
"""
|
||||||
|
Inspect your site configuration at "https://
|
||||||
|
"""
|
||||||
|
|
||||||
|
Scenario: Start the server with port 65432
|
||||||
|
When I run `middleman server --verbose --port 65432` interactively
|
||||||
|
And I stop middleman if the output contains:
|
||||||
|
"""
|
||||||
|
Inspect your site configuration
|
||||||
|
"""
|
||||||
|
Then the output should contain:
|
||||||
|
"""
|
||||||
|
The Middleman preview server is bind to ":::65432", "0.0.0.0:65432"
|
||||||
|
"""
|
||||||
|
|
||||||
|
Scenario: Start the server with port 65432 configured via config.rb
|
||||||
|
Given a file named "config.rb" with:
|
||||||
|
"""
|
||||||
|
set :port, 65432
|
||||||
|
"""
|
||||||
|
When I run `middleman server --verbose` interactively
|
||||||
|
And I stop middleman if the output contains:
|
||||||
|
"""
|
||||||
|
Inspect your site configuration
|
||||||
|
"""
|
||||||
|
Then the output should contain:
|
||||||
|
"""
|
||||||
|
The Middleman preview server is bind to ":::65432", "0.0.0.0:65432"
|
||||||
|
"""
|
||||||
|
|
||||||
|
Scenario: Start the server when port is blocked by other middleman instance
|
||||||
|
Given `middleman server` is running in background
|
||||||
|
When I run `middleman server --verbose` interactively
|
||||||
|
And I stop all commands if the output of the last command contains:
|
||||||
|
"""
|
||||||
|
Inspect your site configuration
|
||||||
|
"""
|
||||||
|
Then the output should contain:
|
||||||
|
"""
|
||||||
|
The Middleman uses a different port
|
||||||
|
"""
|
||||||
|
|
||||||
|
Scenario: Start the server with bind address 1.1.1.1
|
||||||
|
|
||||||
|
This should fail, because "1.1.1.1" is not an interface available on this computer.
|
||||||
|
|
||||||
|
Given a file named ".hosts" with:
|
||||||
|
"""
|
||||||
|
1.1.1.1 www.example.com www
|
||||||
|
"""
|
||||||
|
When I run `middleman server --verbose --bind-address 1.1.1.1` interactively
|
||||||
|
And I stop middleman if the output contains:
|
||||||
|
"""
|
||||||
|
Running Middleman failed:
|
||||||
|
"""
|
||||||
|
Then the output should contain:
|
||||||
|
"""
|
||||||
|
Bind address "1.1.1.1" is not available on your system
|
||||||
|
"""
|
||||||
|
|
||||||
|
Scenario: Start the server with server name www.example.com and bind address 0.0.0.0
|
||||||
|
|
||||||
|
This should fail, because the user can just use `--server-name`. It does
|
||||||
|
not make sense for `middleman` to only listen on `0.0.0.0` (IPv4 all
|
||||||
|
interfaces), but not on `::` (IPv6 all interfaces). There are other tools
|
||||||
|
like `iptables` (Linux-only) or better some `kernel`-configurations to make
|
||||||
|
this possible.
|
||||||
|
|
||||||
|
When I run `middleman server --verbose --server-name www.example.com --bind-address 0.0.0.0` interactively
|
||||||
|
And I stop middleman if the output contains:
|
||||||
|
"""
|
||||||
|
Running Middleman failed:
|
||||||
|
"""
|
||||||
|
Then the output should contain:
|
||||||
|
"""
|
||||||
|
Undefined combination of options "--server-name" and "--bind-address".
|
||||||
|
"""
|
||||||
|
|
||||||
|
Scenario: Start the server with server name "www.example.com" and bind address "127.0.0.1"
|
||||||
|
|
||||||
|
This should fail because the server name does not resolve to the ip address.
|
||||||
|
|
||||||
|
Given a file named ".hosts" with:
|
||||||
|
"""
|
||||||
|
1.1.1.1 www.example.com www
|
||||||
|
"""
|
||||||
|
When I run `middleman server --verbose --server-name www.example.com --bind-address 127.0.0.1` interactively
|
||||||
|
And I stop middleman if the output contains:
|
||||||
|
"""
|
||||||
|
Running Middleman failed:
|
||||||
|
"""
|
||||||
|
Then the output should contain:
|
||||||
|
"""
|
||||||
|
Server name "www.example.com" does not resolve to bind address "127.0.0.1". Please fix that and try again.
|
||||||
|
"""
|
||||||
|
|
||||||
|
Scenario: Start the server with server name "garbage.example.com"
|
||||||
|
When I run `middleman server --verbose --server-name garbage.example.com` interactively
|
||||||
|
And I stop middleman if the output contains:
|
||||||
|
"""
|
||||||
|
Running Middleman failed:
|
||||||
|
"""
|
||||||
|
Then the output should contain:
|
||||||
|
"""
|
||||||
|
Server name "garbage.example.com" does not resolve to an ip address. Please fix that and try again.
|
||||||
|
"""
|
||||||
|
|
||||||
|
Scenario: Start the server with server name "www.example.com" and the network name server is used to resolve the server name
|
||||||
|
Given I have a local hosts file with:
|
||||||
|
"""
|
||||||
|
# empty
|
||||||
|
"""
|
||||||
|
And I start a mdns server with:
|
||||||
|
"""
|
||||||
|
# empty
|
||||||
|
"""
|
||||||
|
And I start a dns server with:
|
||||||
|
"""
|
||||||
|
www.example.com: 127.0.0.1
|
||||||
|
"""
|
||||||
|
When I run `middleman server --verbose --server-name www.example.com` interactively
|
||||||
|
And I stop middleman if the output contains:
|
||||||
|
"""
|
||||||
|
Inspect your site configuration
|
||||||
|
"""
|
||||||
|
Then the output should contain:
|
||||||
|
"""
|
||||||
|
The Middleman preview server is bind to "127.0.0.1:4567"
|
||||||
|
"""
|
||||||
|
And the output should contain:
|
||||||
|
"""
|
||||||
|
View your site at "http://www.example.com:4567", "http://127.0.0.1:4567"
|
||||||
|
"""
|
||||||
|
And the output should contain:
|
||||||
|
"""
|
||||||
|
Inspect your site configuration at "http://www.example.com:4567/__middleman", "http://127.0.0.1:4567/__middleman"
|
||||||
|
"""
|
||||||
|
|
||||||
|
@ruby-2.1
|
||||||
|
Scenario: Start the server with server name "host.local" and the link local name server is used to resolve the server name
|
||||||
|
|
||||||
|
To make the mdns resolver resolve a name, it needs to end with ".local".
|
||||||
|
Otherwise the resolver returns [].
|
||||||
|
|
||||||
|
Given I have a local hosts file with:
|
||||||
|
"""
|
||||||
|
# empty
|
||||||
|
"""
|
||||||
|
And I start a mdns server with:
|
||||||
|
"""
|
||||||
|
host.local: 127.0.0.1
|
||||||
|
"""
|
||||||
|
When I run `middleman server --verbose --server-name host.local` interactively
|
||||||
|
And I stop middleman if the output contains:
|
||||||
|
"""
|
||||||
|
Inspect your site configuration
|
||||||
|
"""
|
||||||
|
Then the output should contain:
|
||||||
|
"""
|
||||||
|
The Middleman preview server is bind to "127.0.0.1:4567"
|
||||||
|
"""
|
||||||
|
And the output should contain:
|
||||||
|
"""
|
||||||
|
View your site at "http://host.local:4567", "http://127.0.0.1:4567"
|
||||||
|
"""
|
||||||
|
And the output should contain:
|
||||||
|
"""
|
||||||
|
Inspect your site configuration at "http://host.local:4567/__middleman", "http://127.0.0.1:4567/__middleman"
|
||||||
|
"""
|
||||||
|
|
||||||
|
@ruby-2.1
|
||||||
|
Scenario: Start the server with server name "host" and the link local name server is used to resolve the server name
|
||||||
|
|
||||||
|
To make the mdns resolver resolve a name, it needs to end with ".local". If
|
||||||
|
a plain hostname is given `middleman` appends ".local" automatically.
|
||||||
|
|
||||||
|
Given I have a local hosts file with:
|
||||||
|
"""
|
||||||
|
# empty
|
||||||
|
"""
|
||||||
|
And I start a mdns server with:
|
||||||
|
"""
|
||||||
|
host.local: 127.0.0.1
|
||||||
|
"""
|
||||||
|
When I run `middleman server --verbose --server-name host` interactively
|
||||||
|
And I stop middleman if the output contains:
|
||||||
|
"""
|
||||||
|
Inspect your site configuration
|
||||||
|
"""
|
||||||
|
Then the output should contain:
|
||||||
|
"""
|
||||||
|
The Middleman preview server is bind to "127.0.0.1:4567"
|
||||||
|
"""
|
||||||
|
And the output should contain:
|
||||||
|
"""
|
||||||
|
View your site at "http://host.local:4567", "http://127.0.0.1:4567"
|
||||||
|
"""
|
||||||
|
And the output should contain:
|
||||||
|
"""
|
||||||
|
Inspect your site configuration at "http://host.local:4567/__middleman", "http://127.0.0.1:4567/__middleman"
|
||||||
|
"""
|
33
middleman-core/fixtures/preview-server-app/bin/dns_server.rb
Executable file
33
middleman-core/fixtures/preview-server-app/bin/dns_server.rb
Executable file
|
@ -0,0 +1,33 @@
|
||||||
|
#!/usr/bin/env ruby
|
||||||
|
|
||||||
|
require 'rubydns'
|
||||||
|
require 'psych'
|
||||||
|
|
||||||
|
db_file = ARGV[0]
|
||||||
|
port = ARGV[1] || 5300
|
||||||
|
|
||||||
|
db = if File.file? db_file
|
||||||
|
$stderr.puts 'Found dns db'
|
||||||
|
Psych.load_file(db_file)
|
||||||
|
else
|
||||||
|
$stderr.puts 'Found no dns db. Use default db.'
|
||||||
|
|
||||||
|
{
|
||||||
|
/www\.example\.org/ => '1.1.1.1'
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
interfaces = [
|
||||||
|
[:udp, "127.0.0.1", port],
|
||||||
|
[:tcp, "127.0.0.1", port]
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
# Start the RubyDNS server
|
||||||
|
RubyDNS::run_server(:listen => interfaces) do
|
||||||
|
db.each do |matcher, result|
|
||||||
|
match(matcher, Resolv::DNS::Resource::IN::A) do |transaction|
|
||||||
|
transaction.respond!(result)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,11 @@
|
||||||
|
page "/fake.html", :proxy => "/real.html", :layout => false
|
||||||
|
|
||||||
|
ignore "/should_be_ignored.html"
|
||||||
|
page "/should_be_ignored2.html", :ignore => true
|
||||||
|
page "/target_ignore.html", :proxy => "/should_be_ignored3.html", :ignore => true
|
||||||
|
|
||||||
|
%w(one two).each do |num|
|
||||||
|
page "/fake/#{num}.html", :proxy => "/real/index.html" do
|
||||||
|
@num = num
|
||||||
|
end
|
||||||
|
end
|
11
middleman-core/fixtures/preview-server-app/config.rb
Normal file
11
middleman-core/fixtures/preview-server-app/config.rb
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
page "/fake.html", :proxy => "/real.html", :layout => false
|
||||||
|
|
||||||
|
ignore "/should_be_ignored.html"
|
||||||
|
page "/should_be_ignored2.html", :ignore => true
|
||||||
|
page "/target_ignore.html", :proxy => "/should_be_ignored3.html", :ignore => true
|
||||||
|
|
||||||
|
%w(one two).each do |num|
|
||||||
|
page "/fake/#{num}.html", :proxy => "/real/index.html" do
|
||||||
|
@num = num
|
||||||
|
end
|
||||||
|
end
|
1
middleman-core/fixtures/preview-server-app/source/index.html.erb
Executable file
1
middleman-core/fixtures/preview-server-app/source/index.html.erb
Executable file
|
@ -0,0 +1 @@
|
||||||
|
<h1>Welcome</h1>
|
|
@ -0,0 +1,9 @@
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>My Sample Site</title>
|
||||||
|
<!-- Comment in layout -->
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<%= yield %>
|
||||||
|
</body>
|
||||||
|
</html>
|
8
middleman-core/fixtures/preview-server-app/source/layouts/custom.erb
Executable file
8
middleman-core/fixtures/preview-server-app/source/layouts/custom.erb
Executable file
|
@ -0,0 +1,8 @@
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Custom Layout</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<%= yield %>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1 @@
|
||||||
|
I am real
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
layout: false
|
||||||
|
---
|
||||||
|
|
||||||
|
I am real: <%= @num %>
|
|
@ -0,0 +1 @@
|
||||||
|
<h1>Ignore me!</h1>
|
|
@ -0,0 +1 @@
|
||||||
|
<h1>Ignore me! 2</h1>
|
|
@ -0,0 +1 @@
|
||||||
|
<h1>Ignore me! 3</h1>
|
1
middleman-core/fixtures/preview-server-app/source/static.html
Executable file
1
middleman-core/fixtures/preview-server-app/source/static.html
Executable file
|
@ -0,0 +1 @@
|
||||||
|
Static, no code!
|
|
@ -71,7 +71,11 @@ module Middleman
|
||||||
|
|
||||||
# Which server name should be used
|
# Which server name should be used
|
||||||
# @return [NilClass, String]
|
# @return [NilClass, String]
|
||||||
config.define_setting :host, nil, 'The preview host name'
|
config.define_setting :server_name, nil, 'The server name of preview server'
|
||||||
|
|
||||||
|
# Which bind address the preview server should use
|
||||||
|
# @return [NilClass, String]
|
||||||
|
config.define_setting :bind_address, nil, 'The bind address of the preview server'
|
||||||
|
|
||||||
# Whether to serve the preview server over HTTPS.
|
# Whether to serve the preview server over HTTPS.
|
||||||
# @return [Boolean]
|
# @return [Boolean]
|
||||||
|
|
|
@ -14,9 +14,12 @@ module Middleman::Cli
|
||||||
method_option :port,
|
method_option :port,
|
||||||
aliases: '-p',
|
aliases: '-p',
|
||||||
desc: 'The port Middleman will listen on'
|
desc: 'The port Middleman will listen on'
|
||||||
method_option :host,
|
method_option :server_name,
|
||||||
aliases: '-h',
|
aliases: '-s',
|
||||||
desc: 'The host name Middleman will use'
|
desc: 'The server name name Middleman will use'
|
||||||
|
method_option :bind_address,
|
||||||
|
aliases: '-b',
|
||||||
|
desc: 'The bind address Middleman will listen on'
|
||||||
method_option :https,
|
method_option :https,
|
||||||
type: :boolean,
|
type: :boolean,
|
||||||
desc: 'Serve the preview server over SSL/TLS'
|
desc: 'Serve the preview server over SSL/TLS'
|
||||||
|
@ -68,8 +71,9 @@ module Middleman::Cli
|
||||||
|
|
||||||
params = {
|
params = {
|
||||||
port: options['port'],
|
port: options['port'],
|
||||||
|
bind_address: options['bind_address'],
|
||||||
https: options['https'],
|
https: options['https'],
|
||||||
host: options['host'],
|
server_name: options['server_name'],
|
||||||
ssl_certificate: options['ssl_certificate'],
|
ssl_certificate: options['ssl_certificate'],
|
||||||
ssl_private_key: options['ssl_private_key'],
|
ssl_private_key: options['ssl_private_key'],
|
||||||
environment: options['environment'],
|
environment: options['environment'],
|
||||||
|
|
73
middleman-core/lib/middleman-core/dns_resolver.rb
Normal file
73
middleman-core/lib/middleman-core/dns_resolver.rb
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
require 'resolv'
|
||||||
|
require 'middleman-core/dns_resolver/network_resolver'
|
||||||
|
require 'middleman-core/dns_resolver/hosts_resolver'
|
||||||
|
|
||||||
|
module Middleman
|
||||||
|
# This resolves IP address to names and vice versa
|
||||||
|
class DnsResolver
|
||||||
|
private
|
||||||
|
|
||||||
|
attr_reader :resolvers
|
||||||
|
|
||||||
|
public
|
||||||
|
|
||||||
|
# Create resolver
|
||||||
|
#
|
||||||
|
# First the local resolver is used. If environment variable HOSTSRC is
|
||||||
|
# given this file is used for local name lookup.
|
||||||
|
#
|
||||||
|
# @param [#getnames, #getaddresses] network_resolver
|
||||||
|
# The resolver which uses a network name server to resolve ip addresses
|
||||||
|
# and names.
|
||||||
|
#
|
||||||
|
# @param [#getnames, #getaddresses] local_resolver
|
||||||
|
# The resolver uses /etc/hosts on POSIX-systems and
|
||||||
|
# C:\Windows\System32\drivers\etc\hosts on Windows-operating systems to
|
||||||
|
# resolve ip addresses and names.
|
||||||
|
#
|
||||||
|
# First the local resolver is queried. If this raises an error or returns
|
||||||
|
# nil or [] the network resolver is queried.
|
||||||
|
def initialize(opts={})
|
||||||
|
@resolvers = []
|
||||||
|
@resolvers << opts.fetch(:hosts_resolver, HostsResolver.new)
|
||||||
|
|
||||||
|
if RUBY_VERSION >= '2.1'
|
||||||
|
require 'middleman-core/dns_resolver/local_link_resolver'
|
||||||
|
@resolvers << opts.fetch(:local_link_resolver, LocalLinkResolver.new)
|
||||||
|
end
|
||||||
|
|
||||||
|
@resolvers << opts.fetch(:network_resolver, NetworkResolver.new)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Get names for given ip
|
||||||
|
#
|
||||||
|
# @param [String] ip
|
||||||
|
# The ip which should be resolved.
|
||||||
|
def names_for(ip)
|
||||||
|
resolvers.each do |r|
|
||||||
|
names = r.getnames(ip)
|
||||||
|
|
||||||
|
return names unless names.nil? || names.empty?
|
||||||
|
end
|
||||||
|
|
||||||
|
[]
|
||||||
|
end
|
||||||
|
|
||||||
|
# Get ips for given name
|
||||||
|
#
|
||||||
|
# First the local resolver is used. On POSIX-systems /etc/hosts is used. On
|
||||||
|
# Windows C:\Windows\System32\drivers\etc\hosts is used.
|
||||||
|
#
|
||||||
|
# @param [String] name
|
||||||
|
# The name which should be resolved.
|
||||||
|
def ips_for(name)
|
||||||
|
resolvers.each do |r|
|
||||||
|
ips = r.getaddresses(name)
|
||||||
|
|
||||||
|
return ips unless ips.nil? || ips.empty?
|
||||||
|
end
|
||||||
|
|
||||||
|
[]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,52 @@
|
||||||
|
module Middleman
|
||||||
|
class DnsResolver
|
||||||
|
# Use network name server to resolve ips and names
|
||||||
|
class BasicNetworkResolver
|
||||||
|
private
|
||||||
|
|
||||||
|
attr_reader :resolver, :timeouts
|
||||||
|
|
||||||
|
public
|
||||||
|
|
||||||
|
def initialize(opts={})
|
||||||
|
@timeouts = opts.fetch(:timeouts, 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Get names for ip
|
||||||
|
#
|
||||||
|
# @param [#to_s] ip
|
||||||
|
# The ip to resolve into names
|
||||||
|
#
|
||||||
|
# @return [Array]
|
||||||
|
# Array of Names
|
||||||
|
def getnames(ip)
|
||||||
|
resolver.getnames(ip.to_s).map(&:to_s)
|
||||||
|
rescue Resolv::ResolvError, Errno::EADDRNOTAVAIL
|
||||||
|
[]
|
||||||
|
end
|
||||||
|
|
||||||
|
# Get ips for name
|
||||||
|
#
|
||||||
|
# @param [#to_s] name
|
||||||
|
# The name to resolve into ips
|
||||||
|
#
|
||||||
|
# @return [Array]
|
||||||
|
# Array of ipaddresses
|
||||||
|
def getaddresses(name)
|
||||||
|
resolver.getaddresses(name.to_s).map(&:to_s)
|
||||||
|
rescue Resolv::ResolvError, Errno::EADDRNOTAVAIL
|
||||||
|
[]
|
||||||
|
end
|
||||||
|
|
||||||
|
# Set timeout for lookup
|
||||||
|
#
|
||||||
|
# @param [Integer] value
|
||||||
|
# The timeout value
|
||||||
|
def timeouts=(timeouts)
|
||||||
|
return if RUBY_VERSION < '2'
|
||||||
|
|
||||||
|
resolver.timeouts = timeouts
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,63 @@
|
||||||
|
module Middleman
|
||||||
|
class DnsResolver
|
||||||
|
# Use network name server to resolve ips and names
|
||||||
|
class HostsResolver
|
||||||
|
private
|
||||||
|
|
||||||
|
attr_reader :resolver
|
||||||
|
|
||||||
|
public
|
||||||
|
|
||||||
|
def initialize(opts={})
|
||||||
|
# using the splat operator works around a non-existing HOSTSRC variable
|
||||||
|
# using nil as input does not work, but `*[]` does and then Resolv::Hosts
|
||||||
|
# uses its defaults
|
||||||
|
@resolver = opts.fetch(:resolver, Resolv::Hosts.new(*hosts_file))
|
||||||
|
end
|
||||||
|
|
||||||
|
# Get names for ip
|
||||||
|
#
|
||||||
|
# @param [#to_s] ip
|
||||||
|
# The ip to resolve into names
|
||||||
|
#
|
||||||
|
# @return [Array]
|
||||||
|
# Array of Names
|
||||||
|
def getnames(ip)
|
||||||
|
resolver.getnames(ip.to_s).map(&:to_s)
|
||||||
|
rescue Resolv::ResolvError
|
||||||
|
[]
|
||||||
|
end
|
||||||
|
|
||||||
|
# Get ips for name
|
||||||
|
#
|
||||||
|
# @param [#to_s] name
|
||||||
|
# The name to resolve into ips
|
||||||
|
#
|
||||||
|
# @return [Array]
|
||||||
|
# Array of ipaddresses
|
||||||
|
def getaddresses(name)
|
||||||
|
resolver.getaddresses(name.to_s).map(&:to_s)
|
||||||
|
rescue Resolv::ResolvError
|
||||||
|
[]
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
# Path to hosts file
|
||||||
|
#
|
||||||
|
# This looks for MM_HOSTSRC in your environment
|
||||||
|
#
|
||||||
|
# @return [Array]
|
||||||
|
# This needs to be an array, to make the splat operator work
|
||||||
|
#
|
||||||
|
# @example
|
||||||
|
# # <ip> <hostname>
|
||||||
|
# 127.0.0.1 localhost.localhost localhost
|
||||||
|
def hosts_file
|
||||||
|
return [ENV['MM_HOSTSRC']] if ENV.key?('MM_HOSTSRC') && File.file?(ENV['MM_HOSTSRC'])
|
||||||
|
|
||||||
|
[]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,44 @@
|
||||||
|
require 'middleman-core/dns_resolver/basic_network_resolver'
|
||||||
|
|
||||||
|
module Middleman
|
||||||
|
class DnsResolver
|
||||||
|
# Use network name server to resolve ips and names
|
||||||
|
class LocalLinkResolver < BasicNetworkResolver
|
||||||
|
def initialize(opts={})
|
||||||
|
super
|
||||||
|
|
||||||
|
@timeouts = opts.fetch(:timeouts, 1)
|
||||||
|
@resolver = opts.fetch(:resolver, Resolv::MDNS.new(nameserver_config))
|
||||||
|
|
||||||
|
self.timeouts = timeouts
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
# Hosts + Ports for MDNS resolver
|
||||||
|
#
|
||||||
|
# This looks for MM_MDNSRC in your environment. If you are going to use
|
||||||
|
# IPv6-addresses: Make sure you do not forget to add the port at the end.
|
||||||
|
#
|
||||||
|
# MM_MDNSRC=ip:port ip:port
|
||||||
|
#
|
||||||
|
# @return [Hash]
|
||||||
|
# Returns the configuration for the nameserver
|
||||||
|
#
|
||||||
|
# @example
|
||||||
|
# export MM_MDNSRC="224.0.0.251:5353 ff02::fb:5353"
|
||||||
|
#
|
||||||
|
def nameserver_config
|
||||||
|
return unless ENV.key?('MM_MDNSRC') && ENV['MM_MDNSRC']
|
||||||
|
|
||||||
|
address, port = ENV['MM_MDNSRC'].split(/:/)
|
||||||
|
|
||||||
|
{
|
||||||
|
nameserver_port: [[address, port.to_i]]
|
||||||
|
}
|
||||||
|
rescue StandardError
|
||||||
|
{}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,42 @@
|
||||||
|
require 'middleman-core/dns_resolver/basic_network_resolver'
|
||||||
|
|
||||||
|
module Middleman
|
||||||
|
class DnsResolver
|
||||||
|
# Use network name server to resolve ips and names
|
||||||
|
class NetworkResolver < BasicNetworkResolver
|
||||||
|
def initialize(opts={})
|
||||||
|
super
|
||||||
|
|
||||||
|
@resolver = opts.fetch(:resolver, Resolv::DNS.new(nameserver_config))
|
||||||
|
self.timeouts = timeouts
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
# Hosts + Ports for MDNS resolver
|
||||||
|
#
|
||||||
|
# This looks for MM_MDNSRC in your environment. If you are going to use
|
||||||
|
# IPv6-addresses: Make sure you do not forget to add the port at the end.
|
||||||
|
#
|
||||||
|
# MM_MDNSRC=ip:port ip:port
|
||||||
|
#
|
||||||
|
# @return [Hash]
|
||||||
|
# Returns the configuration for the nameserver
|
||||||
|
#
|
||||||
|
# @example
|
||||||
|
# export MM_MDNSRC="224.0.0.251:5353 ff02::fb:5353"
|
||||||
|
#
|
||||||
|
def nameserver_config
|
||||||
|
return unless ENV.key?('MM_DNSRC') && ENV['MM_DNSRC']
|
||||||
|
|
||||||
|
address, port = ENV['MM_DNSRC'].split(/:/)
|
||||||
|
|
||||||
|
{
|
||||||
|
nameserver_port: [[address, port.to_i]]
|
||||||
|
}
|
||||||
|
rescue StandardError
|
||||||
|
{}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,15 +1,16 @@
|
||||||
require 'webrick'
|
require 'webrick'
|
||||||
require 'webrick/https'
|
require 'webrick/https'
|
||||||
require 'openssl'
|
require 'openssl'
|
||||||
require 'socket'
|
|
||||||
require 'middleman-core/meta_pages'
|
require 'middleman-core/meta_pages'
|
||||||
require 'middleman-core/logger'
|
require 'middleman-core/logger'
|
||||||
|
require 'middleman-core/preview_server/server_information'
|
||||||
|
require 'middleman-core/preview_server/server_url'
|
||||||
|
|
||||||
# rubocop:disable GlobalVars
|
# rubocop:disable GlobalVars
|
||||||
module Middleman
|
module Middleman
|
||||||
module PreviewServer
|
class PreviewServer
|
||||||
class << self
|
class << self
|
||||||
attr_reader :app, :host, :port, :ssl_certificate, :ssl_private_key, :environment
|
attr_reader :app, :ssl_certificate, :ssl_private_key, :environment, :server_information
|
||||||
delegate :logger, to: :app
|
delegate :logger, to: :app
|
||||||
|
|
||||||
def https?
|
def https?
|
||||||
|
@ -19,13 +20,31 @@ module Middleman
|
||||||
# Start an instance of Middleman::Application
|
# Start an instance of Middleman::Application
|
||||||
# @return [void]
|
# @return [void]
|
||||||
def start(opts={})
|
def start(opts={})
|
||||||
|
# Do not buffer output, otherwise testing of output does not work
|
||||||
|
$stdout.sync = true
|
||||||
|
$stderr.sync = true
|
||||||
|
|
||||||
@options = opts
|
@options = opts
|
||||||
|
@server_information = ServerInformation.new
|
||||||
|
|
||||||
mount_instance(new_app)
|
# New app evaluates the middleman configuration. Since this can be
|
||||||
|
# invalid as well, we need to evaluate the configuration BEFORE
|
||||||
|
# checking for validity
|
||||||
|
the_app = new_app
|
||||||
|
|
||||||
|
# And now comes the check
|
||||||
|
unless server_information.valid?
|
||||||
|
logger.fatal %(== Running Middleman failed: #{server_information.reason}. Please fix that and try again.)
|
||||||
|
exit 1
|
||||||
|
end
|
||||||
|
|
||||||
|
mount_instance(the_app)
|
||||||
|
|
||||||
|
logger.debug %(== Server information is provided by #{server_information.handler})
|
||||||
logger.debug %(== The Middleman is running in "#{environment}" environment)
|
logger.debug %(== The Middleman is running in "#{environment}" environment)
|
||||||
logger.info "== The Middleman is standing watch at #{uri} (#{uri(public_ip)})"
|
logger.debug format('== The Middleman preview server is bind to %s', ServerUrl.new(hosts: server_information.listeners, port: server_information.port, https: https?).to_bind_addresses.join(', '))
|
||||||
logger.info "== Inspect your site configuration at #{uri + '__middleman'}"
|
logger.info format('== View your site at %s', ServerUrl.new(hosts: server_information.site_addresses, port: server_information.port, https: https?).to_urls.join(', '))
|
||||||
|
logger.info format('== Inspect your site configuration at %s', ServerUrl.new(hosts: server_information.site_addresses, port: server_information.port, https: https?).to_config_urls.join(', '))
|
||||||
|
|
||||||
@initialized ||= false
|
@initialized ||= false
|
||||||
return if @initialized
|
return if @initialized
|
||||||
|
@ -116,18 +135,26 @@ module Middleman
|
||||||
opts[:instrumenting] || false
|
opts[:instrumenting] || false
|
||||||
)
|
)
|
||||||
|
|
||||||
config[:environment] = opts[:environment].to_sym if opts[:environment]
|
config[:environment] = opts[:environment].to_sym if opts[:environment]
|
||||||
config[:port] = opts[:port] if opts[:port]
|
config[:port] = opts[:port] if opts[:port]
|
||||||
config[:host] = opts[:host].presence || Socket.gethostname.tr(' ', '+')
|
config[:bind_address] = opts[:bind_address]
|
||||||
config[:https] = opts[:https] unless opts[:https].nil?
|
config[:server_name] = opts[:server_name]
|
||||||
|
config[:https] = opts[:https] unless opts[:https].nil?
|
||||||
config[:ssl_certificate] = opts[:ssl_certificate] if opts[:ssl_certificate]
|
config[:ssl_certificate] = opts[:ssl_certificate] if opts[:ssl_certificate]
|
||||||
config[:ssl_private_key] = opts[:ssl_private_key] if opts[:ssl_private_key]
|
config[:ssl_private_key] = opts[:ssl_private_key] if opts[:ssl_private_key]
|
||||||
end
|
end
|
||||||
|
|
||||||
@host = @app.config[:host]
|
# store configured port to make a check later on possible
|
||||||
@port = @app.config[:port]
|
configured_port = @app.config[:port]
|
||||||
@https = @app.config[:https]
|
|
||||||
@environment = @app.config[:environment]
|
# Use configuration values to set `bind_address` etc. in
|
||||||
|
# `server_information`
|
||||||
|
server_information.use @app.config
|
||||||
|
|
||||||
|
logger.warn format('== The Middleman uses a different port "%s" then the configured one "%s" because some other server is listening on that port.', server_information.port, configured_port) unless @app.config[:port] == configured_port
|
||||||
|
|
||||||
|
@https = @app.config[:https]
|
||||||
|
@environment = @app.config[:environment]
|
||||||
|
|
||||||
@ssl_certificate = @app.config[:ssl_certificate]
|
@ssl_certificate = @app.config[:ssl_certificate]
|
||||||
@ssl_private_key = @app.config[:ssl_private_key]
|
@ssl_private_key = @app.config[:ssl_private_key]
|
||||||
|
@ -190,9 +217,10 @@ module Middleman
|
||||||
# @return [void]
|
# @return [void]
|
||||||
def setup_webrick(is_logging)
|
def setup_webrick(is_logging)
|
||||||
http_opts = {
|
http_opts = {
|
||||||
Port: port,
|
Port: server_information.port,
|
||||||
AccessLog: [],
|
AccessLog: [],
|
||||||
ServerName: host,
|
ServerName: server_information.server_name,
|
||||||
|
BindAddress: server_information.bind_address.to_s,
|
||||||
DoNotReverseLookup: true
|
DoNotReverseLookup: true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -205,7 +233,7 @@ module Middleman
|
||||||
http_opts[:SSLPrivateKey] = OpenSSL::PKey::RSA.new File.read ssl_private_key
|
http_opts[:SSLPrivateKey] = OpenSSL::PKey::RSA.new File.read ssl_private_key
|
||||||
else
|
else
|
||||||
# use a generated self-signed cert
|
# use a generated self-signed cert
|
||||||
cert, key = create_self_signed_cert(1024, [['CN', host]], 'Middleman Preview Server')
|
cert, key = create_self_signed_cert(1024, [['CN', server_information.server_name]], server_information.site_addresses, 'Middleman Preview Server')
|
||||||
http_opts[:SSLCertificate] = cert
|
http_opts[:SSLCertificate] = cert
|
||||||
http_opts[:SSLPrivateKey] = key
|
http_opts[:SSLPrivateKey] = key
|
||||||
end
|
end
|
||||||
|
@ -217,20 +245,17 @@ module Middleman
|
||||||
http_opts[:Logger] = ::WEBrick::Log.new(nil, 0)
|
http_opts[:Logger] = ::WEBrick::Log.new(nil, 0)
|
||||||
end
|
end
|
||||||
|
|
||||||
attempts_left = 4
|
|
||||||
tried_ports = []
|
|
||||||
begin
|
begin
|
||||||
::WEBrick::HTTPServer.new(http_opts)
|
::WEBrick::HTTPServer.new(http_opts)
|
||||||
rescue Errno::EADDRINUSE
|
rescue Errno::EADDRINUSE
|
||||||
logger.error "== Port #{port} is unavailable. Either close the instance of Middleman already running on #{port} or start this Middleman on a new port with: --port=#{unused_tcp_port}"
|
logger.error %(== Port "#{http_opts[:Port]}" is in use. This should not have happened. Please start "middleman server" again.)
|
||||||
exit(1)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Copy of https://github.com/nahi/ruby/blob/webrick_trunk/lib/webrick/ssl.rb#L39
|
# Copy of https://github.com/nahi/ruby/blob/webrick_trunk/lib/webrick/ssl.rb#L39
|
||||||
# that uses a different serial number each time the cert is generated in order to
|
# that uses a different serial number each time the cert is generated in order to
|
||||||
# avoid errors in Firefox. Also doesn't print out stuff to $stderr unnecessarily.
|
# avoid errors in Firefox. Also doesn't print out stuff to $stderr unnecessarily.
|
||||||
def create_self_signed_cert(bits, cn, comment)
|
def create_self_signed_cert(bits, cn, aliases, comment)
|
||||||
rsa = OpenSSL::PKey::RSA.new(bits)
|
rsa = OpenSSL::PKey::RSA.new(bits)
|
||||||
cert = OpenSSL::X509::Certificate.new
|
cert = OpenSSL::X509::Certificate.new
|
||||||
cert.version = 2
|
cert.version = 2
|
||||||
|
@ -254,6 +279,8 @@ module Middleman
|
||||||
aki = ef.create_extension('authorityKeyIdentifier',
|
aki = ef.create_extension('authorityKeyIdentifier',
|
||||||
'keyid:always,issuer:always')
|
'keyid:always,issuer:always')
|
||||||
cert.add_extension(aki)
|
cert.add_extension(aki)
|
||||||
|
cert.add_extension ef.create_extension('subjectAltName', aliases.map { |d| "DNS: #{d}" }.join(','))
|
||||||
|
|
||||||
cert.sign(rsa, OpenSSL::Digest::SHA1.new)
|
cert.sign(rsa, OpenSSL::Digest::SHA1.new)
|
||||||
|
|
||||||
[cert, rsa]
|
[cert, rsa]
|
||||||
|
@ -306,29 +333,6 @@ module Middleman
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns the URI the preview server will run on
|
|
||||||
# @return [URI]
|
|
||||||
def uri(host=@host)
|
|
||||||
scheme = https? ? 'https' : 'http'
|
|
||||||
URI("#{scheme}://#{host}:#{@port}/")
|
|
||||||
end
|
|
||||||
|
|
||||||
# An IPv4 address on this machine which should be externally addressable.
|
|
||||||
# @return [String]
|
|
||||||
def public_ip
|
|
||||||
ip = Socket.ip_address_list.find { |ai| ai.ipv4? && !ai.ipv4_loopback? }
|
|
||||||
ip ? ip.ip_address : '127.0.0.1'
|
|
||||||
end
|
|
||||||
|
|
||||||
# Returns unused TCP port
|
|
||||||
# @return [Fixnum]
|
|
||||||
def unused_tcp_port
|
|
||||||
server = TCPServer.open(0)
|
|
||||||
port = server.addr[1]
|
|
||||||
server.close
|
|
||||||
port
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
class FilteredWebrickLog < ::WEBrick::Log
|
class FilteredWebrickLog < ::WEBrick::Log
|
||||||
|
|
81
middleman-core/lib/middleman-core/preview_server/checks.rb
Normal file
81
middleman-core/lib/middleman-core/preview_server/checks.rb
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
require 'ipaddr'
|
||||||
|
|
||||||
|
module Middleman
|
||||||
|
class PreviewServer
|
||||||
|
# Checks for input of preview server
|
||||||
|
module Checks
|
||||||
|
# This one will get all default setup
|
||||||
|
class BasicCheck; end
|
||||||
|
|
||||||
|
# This checks if the server name resolves to the bind_address
|
||||||
|
#
|
||||||
|
# If the users enters:
|
||||||
|
#
|
||||||
|
# 1. server_name: www.example.com (10.0.0.1)
|
||||||
|
# 2. bind_address: 127.0.0.01
|
||||||
|
#
|
||||||
|
# This validation will fail
|
||||||
|
class ServerNameResolvesToBindAddress < BasicCheck
|
||||||
|
private
|
||||||
|
|
||||||
|
attr_reader :resolver
|
||||||
|
|
||||||
|
public
|
||||||
|
|
||||||
|
def initialize
|
||||||
|
@resolver = DnsResolver.new
|
||||||
|
end
|
||||||
|
|
||||||
|
# Validate
|
||||||
|
#
|
||||||
|
# @param [Information] information
|
||||||
|
# The information to be validated
|
||||||
|
def validate(information)
|
||||||
|
return if resolver.ips_for(information.server_name).include? information.bind_address
|
||||||
|
|
||||||
|
information.valid = false
|
||||||
|
information.reason = format('Server name "%s" does not resolve to bind address "%s"', information.server_name, information.bind_address)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# This validation fails if the user chooses to use an ip address which is
|
||||||
|
# not available on his/her system
|
||||||
|
class InterfaceIsAvailableOnSystem < BasicCheck
|
||||||
|
# Validate
|
||||||
|
#
|
||||||
|
# @param [Information] information
|
||||||
|
# The information to be validated
|
||||||
|
def validate(information)
|
||||||
|
return if information.bind_address.blank? || information.local_network_interfaces.include?(information.bind_address.to_s) || %w(0.0.0.0 ::).any? { |b| information.bind_address == b } || IPAddr.new('127.0.0.0/8').include?(information.bind_address.to_s)
|
||||||
|
|
||||||
|
information.valid = false
|
||||||
|
information.reason = format('Bind address "%s" is not available on your system. Please use one of %s', information.bind_address, information.local_network_interfaces.map { |i| %("#{i}") }.join(', '))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# This one requires a bind address if the user entered a server name
|
||||||
|
#
|
||||||
|
# If the `bind_address` is blank this check will fail
|
||||||
|
class RequiresBindAddressIfServerNameIsGiven < BasicCheck
|
||||||
|
def validate(information)
|
||||||
|
return unless information.bind_address.blank?
|
||||||
|
|
||||||
|
information.valid = false
|
||||||
|
information.reason = format('Server name "%s" does not resolve to an ip address', information.server_name)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# This validation always fails
|
||||||
|
class DenyAnyAny < BasicCheck
|
||||||
|
# Validate
|
||||||
|
#
|
||||||
|
# @param [Information] information
|
||||||
|
# The information to be validated
|
||||||
|
def validate(information)
|
||||||
|
information.valid = false
|
||||||
|
information.reason = 'Undefined combination of options "--server-name" and "--bind-address". If you think this is wrong, please file a bug at "https://github.com/middleman/middleman"'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
273
middleman-core/lib/middleman-core/preview_server/information.rb
Normal file
273
middleman-core/lib/middleman-core/preview_server/information.rb
Normal file
|
@ -0,0 +1,273 @@
|
||||||
|
require 'ipaddr'
|
||||||
|
require 'active_support/core_ext/object/blank'
|
||||||
|
require 'middleman-core/preview_server/checks'
|
||||||
|
require 'middleman-core/preview_server/server_hostname'
|
||||||
|
require 'middleman-core/preview_server/server_ip_address'
|
||||||
|
|
||||||
|
module Middleman
|
||||||
|
class PreviewServer
|
||||||
|
# Basic information class to wrap common behaviour
|
||||||
|
class BasicInformation
|
||||||
|
private
|
||||||
|
|
||||||
|
attr_reader :checks, :network_interfaces_inventory
|
||||||
|
|
||||||
|
public
|
||||||
|
|
||||||
|
attr_accessor :bind_address, :server_name, :port, :reason, :valid
|
||||||
|
attr_reader :listeners, :site_addresses
|
||||||
|
|
||||||
|
# Create instance
|
||||||
|
#
|
||||||
|
# @param [String] bind_address
|
||||||
|
# The bind address of the server
|
||||||
|
#
|
||||||
|
# @param [String] server_name
|
||||||
|
# The name of the server
|
||||||
|
#
|
||||||
|
# @param [Integer] port
|
||||||
|
# The port to listen on
|
||||||
|
def initialize(opts={})
|
||||||
|
@bind_address = ServerIpAddress.new(opts[:bind_address])
|
||||||
|
@server_name = ServerHostname.new(opts[:server_name])
|
||||||
|
@port = opts[:port]
|
||||||
|
@valid = true
|
||||||
|
|
||||||
|
@site_addresses = []
|
||||||
|
@listeners = []
|
||||||
|
@checks = []
|
||||||
|
|
||||||
|
# This needs to be check for each use case. Otherwise `Webrick` will
|
||||||
|
# complain about that.
|
||||||
|
@checks << Checks::InterfaceIsAvailableOnSystem.new
|
||||||
|
end
|
||||||
|
|
||||||
|
# Is the given information valid?
|
||||||
|
def valid?
|
||||||
|
valid == true
|
||||||
|
end
|
||||||
|
|
||||||
|
# Pass "self" to validator
|
||||||
|
#
|
||||||
|
# @param [#validate] validator
|
||||||
|
# The validator
|
||||||
|
def validate_me(validator)
|
||||||
|
validator.validate self, checks
|
||||||
|
end
|
||||||
|
|
||||||
|
def resolve_me(*)
|
||||||
|
fail NoMethodError
|
||||||
|
end
|
||||||
|
|
||||||
|
# Get network information
|
||||||
|
#
|
||||||
|
# @param [#network_interfaces] inventory
|
||||||
|
# Get list of available network interfaces
|
||||||
|
def show_me_network_interfaces(inventory)
|
||||||
|
@network_interfaces_inventory = inventory
|
||||||
|
end
|
||||||
|
|
||||||
|
# Default is to get all network interfaces
|
||||||
|
def local_network_interfaces
|
||||||
|
network_interfaces_inventory.nil? ? [] : network_interfaces_inventory.network_interfaces(:all)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# This only is used if no other parser is available
|
||||||
|
#
|
||||||
|
# The "default" behaviour is to fail because of "Checks::DenyAnyAny"
|
||||||
|
class DefaultInformation < BasicInformation
|
||||||
|
def initialize(*args)
|
||||||
|
super
|
||||||
|
|
||||||
|
# Make this fail
|
||||||
|
@checks << Checks::DenyAnyAny.new
|
||||||
|
end
|
||||||
|
|
||||||
|
def resolve_me(*); end
|
||||||
|
|
||||||
|
# Always true
|
||||||
|
def self.matches?(*)
|
||||||
|
true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# This one is used if no bind address and no server name is given
|
||||||
|
class AllInterfaces < BasicInformation
|
||||||
|
def initialize(*args)
|
||||||
|
super
|
||||||
|
|
||||||
|
after_init
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.matches?(opts={})
|
||||||
|
opts[:bind_address].blank? && opts[:server_name].blank?
|
||||||
|
end
|
||||||
|
|
||||||
|
# Resolve ips
|
||||||
|
def resolve_me(resolver)
|
||||||
|
hostname = ServerHostname.new(Socket.gethostname)
|
||||||
|
hostname_ips = resolver.ips_for(hostname)
|
||||||
|
network_interface = ServerIpAddress.new(Array(local_network_interfaces).first)
|
||||||
|
resolved_name = ServerHostname.new(resolver.names_for(network_interface).first)
|
||||||
|
|
||||||
|
if includes_array? local_network_interfaces, hostname_ips
|
||||||
|
@server_name = hostname
|
||||||
|
@site_addresses << hostname
|
||||||
|
|
||||||
|
network_interface = ServerIpAddress.new((local_network_interfaces & hostname_ips).first)
|
||||||
|
elsif RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/
|
||||||
|
@server_name = hostname
|
||||||
|
@site_addresses << hostname
|
||||||
|
elsif !resolved_name.blank?
|
||||||
|
@server_name = resolved_name
|
||||||
|
@site_addresses << resolved_name
|
||||||
|
else
|
||||||
|
@server_name = network_interface
|
||||||
|
end
|
||||||
|
|
||||||
|
@site_addresses << network_interface
|
||||||
|
|
||||||
|
self
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def includes_array?(a, b)
|
||||||
|
!(a & b).empty?
|
||||||
|
end
|
||||||
|
|
||||||
|
def after_init
|
||||||
|
@listeners << ServerIpAddress.new('::')
|
||||||
|
@listeners << ServerIpAddress.new('0.0.0.0')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# This is used if bind address is 0.0.0.0, the server name needs to be
|
||||||
|
# blank
|
||||||
|
class AllIpv4Interfaces < AllInterfaces
|
||||||
|
def self.matches?(opts={})
|
||||||
|
opts[:bind_address] == '0.0.0.0' && opts[:server_name].blank?
|
||||||
|
end
|
||||||
|
|
||||||
|
# Use only ipv4 interfaces
|
||||||
|
def local_network_interfaces
|
||||||
|
network_interfaces_inventory.nil? ? [] : network_interfaces_inventory.network_interfaces(:ipv4)
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def after_init
|
||||||
|
@listeners << ServerIpAddress.new('0.0.0.0')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# This is used if bind address is ::, the server name needs to be blank
|
||||||
|
class AllIpv6Interfaces < AllInterfaces
|
||||||
|
def self.matches?(opts={})
|
||||||
|
opts[:bind_address] == '::' && opts[:server_name].blank?
|
||||||
|
end
|
||||||
|
|
||||||
|
# Use only ipv6 interfaces
|
||||||
|
def local_network_interfaces
|
||||||
|
network_interfaces_inventory.nil? ? [] : network_interfaces_inventory.network_interfaces(:ipv6)
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def after_init
|
||||||
|
@listeners << ServerIpAddress.new('::')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Used if a bind address is given and the server name is blank
|
||||||
|
class BindAddressInformation < BasicInformation
|
||||||
|
def initialize(*args)
|
||||||
|
super
|
||||||
|
|
||||||
|
@listeners << bind_address
|
||||||
|
@site_addresses << bind_address
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.matches?(opts={})
|
||||||
|
!opts[:bind_address].blank? && opts[:server_name].blank?
|
||||||
|
end
|
||||||
|
|
||||||
|
# Resolv
|
||||||
|
def resolve_me(resolver)
|
||||||
|
@server_name = ServerHostname.new(resolver.names_for(bind_address).first)
|
||||||
|
@site_addresses << @server_name unless @server_name.blank?
|
||||||
|
|
||||||
|
self
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Use if server name is given and bind address is blank
|
||||||
|
class ServerNameInformation < BasicInformation
|
||||||
|
def initialize(*args)
|
||||||
|
super
|
||||||
|
|
||||||
|
@checks << Checks::RequiresBindAddressIfServerNameIsGiven.new
|
||||||
|
@site_addresses << server_name
|
||||||
|
end
|
||||||
|
|
||||||
|
def resolve_me(resolver)
|
||||||
|
@bind_address = ServerIpAddress.new(resolver.ips_for(server_name).first)
|
||||||
|
|
||||||
|
unless bind_address.blank?
|
||||||
|
@listeners << bind_address
|
||||||
|
@site_addresses << bind_address
|
||||||
|
end
|
||||||
|
|
||||||
|
self
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.matches?(opts={})
|
||||||
|
opts[:bind_address].blank? && !opts[:server_name].blank?
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Only used if bind address and server name are given and bind address is
|
||||||
|
# not :: or 0.0.0.0
|
||||||
|
class BindAddressAndServerNameInformation < BasicInformation
|
||||||
|
def initialize(*args)
|
||||||
|
super
|
||||||
|
|
||||||
|
@listeners << bind_address
|
||||||
|
@site_addresses << server_name
|
||||||
|
@site_addresses << bind_address
|
||||||
|
|
||||||
|
@checks << Checks::ServerNameResolvesToBindAddress.new
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.matches?(opts={})
|
||||||
|
!opts[:bind_address].blank? && !opts[:server_name].blank? && !%w(:: 0.0.0.0).include?(opts[:bind_address])
|
||||||
|
end
|
||||||
|
|
||||||
|
def resolve_me(*); end
|
||||||
|
end
|
||||||
|
|
||||||
|
# If the server name is either an ipv4 or ipv6 address, e.g. 127.0.0.1 or
|
||||||
|
# ::1, use this one
|
||||||
|
class ServerNameIsIpInformation < BasicInformation
|
||||||
|
def initialize(opts={})
|
||||||
|
super
|
||||||
|
|
||||||
|
ip = ServerIpAddress.new(server_name.to_s)
|
||||||
|
|
||||||
|
@listeners << ip
|
||||||
|
@site_addresses << ip
|
||||||
|
end
|
||||||
|
|
||||||
|
def resolve_me(*); end
|
||||||
|
|
||||||
|
def self.matches?(opts={})
|
||||||
|
ip = IPAddr.new(opts[:server_name])
|
||||||
|
|
||||||
|
ip.ipv4? || ip.ipv6?
|
||||||
|
rescue
|
||||||
|
false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,65 @@
|
||||||
|
require 'middleman-core/preview_server/server_ip_address'
|
||||||
|
|
||||||
|
module Middleman
|
||||||
|
class PreviewServer
|
||||||
|
# This holds information about local network interfaces on the user systemd
|
||||||
|
class NetworkInterfaceInventory
|
||||||
|
# Return all ip interfaces
|
||||||
|
class All
|
||||||
|
def network_interfaces
|
||||||
|
ipv4_addresses = Socket.ip_address_list.select(&:ipv4?).map { |ai| ServerIpv4Address.new(ai.ip_address) }
|
||||||
|
ipv6_addresses = Socket.ip_address_list.select(&:ipv6?).map { |ai| ServerIpv6Address.new(ai.ip_address) }
|
||||||
|
|
||||||
|
ipv4_addresses + ipv6_addresses
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.match?(*)
|
||||||
|
true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Return all ipv4 interfaces
|
||||||
|
class Ipv4
|
||||||
|
def network_interfaces
|
||||||
|
Socket.ip_address_list.select { |ai| ai.ipv4? && !ai.ipv4_loopback? }.map { |ai| ServerIpv4Address.new(ai.ip_address) }
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.match?(type)
|
||||||
|
:ipv4 == type
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Return all ipv6 interfaces
|
||||||
|
class Ipv6
|
||||||
|
def network_interfaces
|
||||||
|
Socket.ip_address_list.select { |ai| ai.ipv6? && !ai.ipv6_loopback? }.map { |ai| ServerIpv6Address.new(ai.ip_address) }
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.match?(type)
|
||||||
|
:ipv6 == type
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
attr_reader :types
|
||||||
|
|
||||||
|
public
|
||||||
|
|
||||||
|
def initialize
|
||||||
|
@types = []
|
||||||
|
@types << Ipv4
|
||||||
|
@types << Ipv6
|
||||||
|
@types << All
|
||||||
|
end
|
||||||
|
|
||||||
|
# Return ip interfaces
|
||||||
|
#
|
||||||
|
# @param [Symbol] type
|
||||||
|
# The type of interface which should be returned
|
||||||
|
def network_interfaces(type=:all)
|
||||||
|
types.find { |t| t.match? type.to_sym }.new.network_interfaces
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,39 @@
|
||||||
|
module Middleman
|
||||||
|
class PreviewServer
|
||||||
|
class ServerHostname
|
||||||
|
class ServerFullHostname < SimpleDelegator
|
||||||
|
def to_s
|
||||||
|
__getobj__
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.match?(*)
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
|
alias_method :to_browser, :to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
class ServerPlainHostname < SimpleDelegator
|
||||||
|
def to_s
|
||||||
|
__getobj__ + '.local'
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.match?(name)
|
||||||
|
# rubocop:disable Style/CaseEquality
|
||||||
|
name != 'localhost' && /^(?=.{1,255}$)[0-9A-Za-z](?:(?:[0-9A-Za-z]|-){0,61}[0-9A-Za-z])?\.?$/ === name
|
||||||
|
# rubocop:enable Style/CaseEquality
|
||||||
|
end
|
||||||
|
|
||||||
|
alias_method :to_browser, :to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.new(string)
|
||||||
|
@names = []
|
||||||
|
@names << ServerPlainHostname
|
||||||
|
@names << ServerFullHostname
|
||||||
|
|
||||||
|
@names.find { |n| n.match? string }.new(string)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,144 @@
|
||||||
|
require 'middleman-core/dns_resolver'
|
||||||
|
require 'middleman-core/preview_server/information'
|
||||||
|
require 'middleman-core/preview_server/network_interface_inventory'
|
||||||
|
require 'middleman-core/preview_server/tcp_port_prober'
|
||||||
|
require 'middleman-core/preview_server/server_information_validator'
|
||||||
|
|
||||||
|
module Middleman
|
||||||
|
class PreviewServer
|
||||||
|
# This class holds all information which the preview server needs to setup a listener
|
||||||
|
#
|
||||||
|
# * server name
|
||||||
|
# * bind address
|
||||||
|
# * port
|
||||||
|
#
|
||||||
|
# Furthermore it probes for a free tcp port, if the default one 4567 is not available.
|
||||||
|
class ServerInformation
|
||||||
|
private
|
||||||
|
|
||||||
|
attr_reader :resolver, :validator, :network_interface_inventory, :informations, :tcp_port_prober
|
||||||
|
|
||||||
|
public
|
||||||
|
|
||||||
|
def initialize(opts={})
|
||||||
|
@resolver = opts.fetch(:resolver, DnsResolver.new)
|
||||||
|
@validator = opts.fetch(:validator, ServerInformationValidator.new)
|
||||||
|
@network_interface_inventory = opts.fetch(:network_interface_inventory, NetworkInterfaceInventory.new)
|
||||||
|
@tcp_port_prober = opts.fetch(:tcp_port_prober, TcpPortProber.new)
|
||||||
|
|
||||||
|
@informations = []
|
||||||
|
@informations << AllInterfaces
|
||||||
|
@informations << AllIpv4Interfaces
|
||||||
|
@informations << AllIpv6Interfaces
|
||||||
|
@informations << ServerNameIsIpInformation
|
||||||
|
@informations << ServerNameInformation
|
||||||
|
@informations << BindAddressInformation
|
||||||
|
@informations << BindAddressAndServerNameInformation
|
||||||
|
@informations << DefaultInformation
|
||||||
|
end
|
||||||
|
|
||||||
|
# The information
|
||||||
|
#
|
||||||
|
# Is cached
|
||||||
|
def information
|
||||||
|
return @information if @information
|
||||||
|
|
||||||
|
# The `DefaultInformation`-class always returns `true`, so there's
|
||||||
|
# always a klass available and find will never return nil
|
||||||
|
listener_klass = informations.find { |l| l.matches? bind_address: @bind_address, server_name: @server_name }
|
||||||
|
@information = listener_klass.new(bind_address: @bind_address, server_name: @server_name)
|
||||||
|
|
||||||
|
@information.show_me_network_interfaces(network_interface_inventory)
|
||||||
|
@information.resolve_me(resolver)
|
||||||
|
@information.port = tcp_port_prober.port(@port)
|
||||||
|
@information.validate_me(validator)
|
||||||
|
|
||||||
|
@information
|
||||||
|
end
|
||||||
|
|
||||||
|
# Use a middleman configuration to get information
|
||||||
|
#
|
||||||
|
# @param [#[]] config
|
||||||
|
# The middleman config
|
||||||
|
def use(config)
|
||||||
|
@bind_address = config[:bind_address]
|
||||||
|
@port = config[:port]
|
||||||
|
@server_name = config[:server_name]
|
||||||
|
|
||||||
|
config[:bind_address] = bind_address
|
||||||
|
config[:port] = port
|
||||||
|
config[:server_name] = server_name
|
||||||
|
end
|
||||||
|
|
||||||
|
# Make information of internal server class avaible to make debugging
|
||||||
|
# easier. This can be used to log the class which was used to determine
|
||||||
|
# the preview server settings
|
||||||
|
#
|
||||||
|
# @return [String]
|
||||||
|
# The name of the class
|
||||||
|
def handler
|
||||||
|
information.class.to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
# Is the server information valid?
|
||||||
|
#
|
||||||
|
# This is used to output a helpful error message, which can be stored in
|
||||||
|
# `#reason`.
|
||||||
|
#
|
||||||
|
# @return [TrueClass, FalseClass]
|
||||||
|
# The result
|
||||||
|
def valid?
|
||||||
|
information.valid?
|
||||||
|
end
|
||||||
|
|
||||||
|
# The reason why the information is NOT valid
|
||||||
|
#
|
||||||
|
# @return [String]
|
||||||
|
# The reason why the information is not valid
|
||||||
|
def reason
|
||||||
|
information.reason
|
||||||
|
end
|
||||||
|
|
||||||
|
# The server name
|
||||||
|
#
|
||||||
|
# @return [String]
|
||||||
|
# The name of the server
|
||||||
|
def server_name
|
||||||
|
information.server_name
|
||||||
|
end
|
||||||
|
|
||||||
|
# The bind address of server
|
||||||
|
#
|
||||||
|
# @return [String]
|
||||||
|
# The bind address of the server
|
||||||
|
def bind_address
|
||||||
|
information.bind_address
|
||||||
|
end
|
||||||
|
|
||||||
|
# The port on which the server should listen
|
||||||
|
#
|
||||||
|
# @return [Integer]
|
||||||
|
# The port number
|
||||||
|
def port
|
||||||
|
information.port
|
||||||
|
end
|
||||||
|
|
||||||
|
# A list of site addresses
|
||||||
|
#
|
||||||
|
# @return [Array]
|
||||||
|
# A list of addresses which can be used to access the middleman preview
|
||||||
|
# server
|
||||||
|
def site_addresses
|
||||||
|
information.site_addresses
|
||||||
|
end
|
||||||
|
|
||||||
|
# A list of listeners
|
||||||
|
#
|
||||||
|
# @return [Array]
|
||||||
|
# A list of bind address where the
|
||||||
|
def listeners
|
||||||
|
information.listeners
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,18 @@
|
||||||
|
module Middleman
|
||||||
|
class PreviewServer
|
||||||
|
# Validate user input
|
||||||
|
class ServerInformationValidator
|
||||||
|
# Validate the input
|
||||||
|
#
|
||||||
|
# @param [ServerInformation] information
|
||||||
|
# The information instance which holds information about the preview
|
||||||
|
# server settings
|
||||||
|
#
|
||||||
|
# @param [Array] checks
|
||||||
|
# A list of checks which should be evaluated
|
||||||
|
def validate(information, checks)
|
||||||
|
checks.each { |c| c.validate information }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,55 @@
|
||||||
|
require 'ipaddr'
|
||||||
|
require 'forwardable'
|
||||||
|
|
||||||
|
module Middleman
|
||||||
|
class PreviewServer
|
||||||
|
class ServerIpAddress
|
||||||
|
def self.new(ip_address)
|
||||||
|
@parser = []
|
||||||
|
@parser << ServerIpv6Address
|
||||||
|
@parser << ServerIpv4Address
|
||||||
|
|
||||||
|
@parser.find { |p| p.match? ip_address }.new(ip_address)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class BasicServerIpAddress < SimpleDelegator
|
||||||
|
end
|
||||||
|
|
||||||
|
class ServerIpv4Address < BasicServerIpAddress
|
||||||
|
def to_browser
|
||||||
|
__getobj__.to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.match?(*)
|
||||||
|
true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class ServerIpv6Address < BasicServerIpAddress
|
||||||
|
def to_s
|
||||||
|
__getobj__.sub(/%.*$/, '')
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_browser
|
||||||
|
format('[%s]', to_s)
|
||||||
|
end
|
||||||
|
|
||||||
|
if RUBY_VERSION < '2'
|
||||||
|
def self.match?(str)
|
||||||
|
str = str.to_s.sub(/%.*$/, '')
|
||||||
|
IPAddr.new(str).ipv6?
|
||||||
|
rescue StandardError
|
||||||
|
false
|
||||||
|
end
|
||||||
|
else
|
||||||
|
def self.match?(str)
|
||||||
|
str = str.to_s.sub(/%.*$/, '')
|
||||||
|
IPAddr.new(str).ipv6?
|
||||||
|
rescue IPAddr::InvalidAddressError, IPAddr::AddressFamilyError
|
||||||
|
false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,50 @@
|
||||||
|
require 'ipaddr'
|
||||||
|
|
||||||
|
module Middleman
|
||||||
|
class PreviewServer
|
||||||
|
# This builds the server urls for the preview server
|
||||||
|
class ServerUrl
|
||||||
|
private
|
||||||
|
|
||||||
|
attr_reader :hosts, :port, :https
|
||||||
|
|
||||||
|
public
|
||||||
|
|
||||||
|
def initialize(opts={})
|
||||||
|
@hosts = opts.fetch(:hosts)
|
||||||
|
@port = opts.fetch(:port)
|
||||||
|
@https = opts.fetch(:https, false)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Return bind addresses
|
||||||
|
#
|
||||||
|
# @return [Array]
|
||||||
|
# List of bind addresses of format host:port
|
||||||
|
def to_bind_addresses
|
||||||
|
hosts.map { |l| format('"%s:%s"', l.to_s, port) }
|
||||||
|
end
|
||||||
|
|
||||||
|
# Return server urls
|
||||||
|
#
|
||||||
|
# @return [Array]
|
||||||
|
# List of urls of format http://host:port
|
||||||
|
def to_urls
|
||||||
|
hosts.map { |l| format('"%s://%s:%s"', https? ? 'https' : 'http', l.to_browser, port) }
|
||||||
|
end
|
||||||
|
|
||||||
|
# Return server config urls
|
||||||
|
#
|
||||||
|
# @return [Array]
|
||||||
|
# List of urls of format http://host:port/__middleman
|
||||||
|
def to_config_urls
|
||||||
|
hosts.map { |l| format('"%s://%s:%s/__middleman"', https? ? 'https' : 'http', l.to_browser, port) }
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def https?
|
||||||
|
https == true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,29 @@
|
||||||
|
module Middleman
|
||||||
|
class PreviewServer
|
||||||
|
# Probe for tcp ports
|
||||||
|
#
|
||||||
|
# This one first tries `try_port` if this is not available use the free
|
||||||
|
# port returned by TCPServer.
|
||||||
|
class TcpPortProber
|
||||||
|
# Check for port
|
||||||
|
#
|
||||||
|
# @param [Integer] try_port
|
||||||
|
# The port to be checked
|
||||||
|
#
|
||||||
|
# @return [Integer]
|
||||||
|
# The port
|
||||||
|
def port(try_port)
|
||||||
|
server = TCPServer.open(try_port)
|
||||||
|
server.close
|
||||||
|
|
||||||
|
try_port
|
||||||
|
rescue
|
||||||
|
server = TCPServer.open(0)
|
||||||
|
port = server.addr[1]
|
||||||
|
server.close
|
||||||
|
|
||||||
|
port
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -3,6 +3,7 @@ require 'aruba/jruby'
|
||||||
require 'middleman-core/step_definitions/middleman_steps'
|
require 'middleman-core/step_definitions/middleman_steps'
|
||||||
require 'middleman-core/step_definitions/builder_steps'
|
require 'middleman-core/step_definitions/builder_steps'
|
||||||
require 'middleman-core/step_definitions/server_steps'
|
require 'middleman-core/step_definitions/server_steps'
|
||||||
|
require 'middleman-core/step_definitions/commandline_steps'
|
||||||
|
|
||||||
# Monkeypatch for windows support
|
# Monkeypatch for windows support
|
||||||
module ArubaMonkeypatch
|
module ArubaMonkeypatch
|
||||||
|
|
|
@ -0,0 +1,88 @@
|
||||||
|
When /^I stop (?:middleman|all commands) if the output( of the last command)? contains:$/ do |last_command, expected|
|
||||||
|
begin
|
||||||
|
Timeout.timeout(exit_timeout) do
|
||||||
|
loop do
|
||||||
|
fail "You need to start middleman interactively first." unless @interactive
|
||||||
|
|
||||||
|
if unescape(@interactive.output) =~ Regexp.new(unescape(expected))
|
||||||
|
only_processes.each { |p| p.terminate }
|
||||||
|
break
|
||||||
|
end
|
||||||
|
|
||||||
|
sleep 0.1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
rescue ChildProcess::TimeoutError, TimeoutError
|
||||||
|
@interactive.terminate
|
||||||
|
ensure
|
||||||
|
announcer.stdout @interactive.stdout
|
||||||
|
announcer.stderr @interactive.stderr
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Make it just a long running process
|
||||||
|
Given /`(.*?)` is running in background/ do |cmd|
|
||||||
|
run(cmd, 120)
|
||||||
|
end
|
||||||
|
|
||||||
|
Given /I have a local hosts file with:/ do |string|
|
||||||
|
step 'I set the environment variables to:', table(
|
||||||
|
%(
|
||||||
|
| variable | value |
|
||||||
|
| MM_HOSTSRC | .hosts |
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
step 'a file named ".hosts" with:', string
|
||||||
|
end
|
||||||
|
|
||||||
|
Given /I start a dns server with:/ do |string|
|
||||||
|
@dns_server.terminate if defined? @dns_server
|
||||||
|
|
||||||
|
port = 5300
|
||||||
|
db_file = 'dns.db'
|
||||||
|
|
||||||
|
step 'I set the environment variables to:', table(
|
||||||
|
%(
|
||||||
|
| variable | value |
|
||||||
|
| MM_DNSRC | 127.0.0.1:#{port}|
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
set_env 'PATH', File.expand_path(File.join(current_dir, 'bin')) + ':' + ENV['PATH']
|
||||||
|
write_file db_file, string
|
||||||
|
|
||||||
|
@dns_server = run("dns_server.rb #{db_file} #{port}", 120)
|
||||||
|
end
|
||||||
|
|
||||||
|
Given /I start a mdns server with:/ do |string|
|
||||||
|
@mdns_server.terminate if defined? @mdns_server
|
||||||
|
|
||||||
|
port = 5301
|
||||||
|
db_file = 'mdns.db'
|
||||||
|
|
||||||
|
step 'I set the environment variables to:', table(
|
||||||
|
%(
|
||||||
|
| variable | value |
|
||||||
|
| MM_MDNSRC | 127.0.0.1:#{port}|
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
set_env 'PATH', File.expand_path(File.join(current_dir, 'bin')) + ':' + ENV['PATH']
|
||||||
|
write_file db_file, string
|
||||||
|
|
||||||
|
@mdns_server = run("dns_server.rb #{db_file} #{port}", 120)
|
||||||
|
end
|
||||||
|
|
||||||
|
Given /I start a mdns server for the local hostname/ do
|
||||||
|
step %(I start a mdns server with:), "#{Socket.gethostname}: 127.0.0.1"
|
||||||
|
end
|
||||||
|
|
||||||
|
# Make sure each and every process is really dead
|
||||||
|
After do
|
||||||
|
only_processes.each { |p| p.terminate }
|
||||||
|
end
|
||||||
|
|
||||||
|
Before '@ruby-2.1' do
|
||||||
|
skip_this_scenario if RUBY_VERSION < '2.1'
|
||||||
|
end
|
118
middleman-core/spec/middleman-core/dns_resolver_spec.rb
Normal file
118
middleman-core/spec/middleman-core/dns_resolver_spec.rb
Normal file
|
@ -0,0 +1,118 @@
|
||||||
|
require 'spec_helper'
|
||||||
|
require 'middleman-core/dns_resolver'
|
||||||
|
|
||||||
|
RSpec.describe Middleman::DnsResolver do
|
||||||
|
subject(:resolver) do
|
||||||
|
described_class.new(
|
||||||
|
hosts_resolver: hosts_resolver,
|
||||||
|
local_link_resolver: local_link_resolver,
|
||||||
|
network_resolver: network_resolver
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:hosts_resolver) { instance_double('Middleman::DnsResolver::HostsResolver') }
|
||||||
|
let(:local_link_resolver) { instance_double('Middleman::DnsResolver::LocalLinkResolver') }
|
||||||
|
let(:network_resolver) { instance_double('Middleman::DnsResolver::NetworkResolver') }
|
||||||
|
|
||||||
|
before :each do
|
||||||
|
allow(network_resolver).to receive(:timeouts=)
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#names_for' do
|
||||||
|
context 'when hosts resolver can resolve name' do
|
||||||
|
before :each do
|
||||||
|
expect(hosts_resolver).to receive(:getnames).with(unresolved_ip).and_return(resolved_names)
|
||||||
|
if RUBY_VERSION >= '2.1'
|
||||||
|
expect(local_link_resolver).not_to receive(:getnames)
|
||||||
|
end
|
||||||
|
expect(network_resolver).not_to receive(:getnames)
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:unresolved_ip) { '127.0.0.1' }
|
||||||
|
let(:resolved_names) { %w(localhost) }
|
||||||
|
|
||||||
|
it { expect(resolver.names_for(unresolved_ip)).to eq resolved_names }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when local link resolver can resolve name' do
|
||||||
|
before :each do
|
||||||
|
expect(hosts_resolver).to receive(:getnames).with(unresolved_ip).and_return([])
|
||||||
|
if RUBY_VERSION >= '2.1'
|
||||||
|
expect(local_link_resolver).to receive(:getnames).with(unresolved_ip).and_return(resolved_names)
|
||||||
|
expect(network_resolver).not_to receive(:getnames)
|
||||||
|
else
|
||||||
|
expect(network_resolver).to receive(:getnames).with(unresolved_ip).and_return(resolved_names)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:unresolved_ip) { '127.0.0.1' }
|
||||||
|
let(:resolved_names) { %w(localhost) }
|
||||||
|
|
||||||
|
it { expect(resolver.names_for(unresolved_ip)).to eq resolved_names }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when network resolver can resolve name' do
|
||||||
|
before :each do
|
||||||
|
expect(hosts_resolver).to receive(:getnames).with(unresolved_ip).and_return([])
|
||||||
|
if RUBY_VERSION >= '2.1'
|
||||||
|
expect(local_link_resolver).to receive(:getnames).with(unresolved_ip).and_return([])
|
||||||
|
end
|
||||||
|
expect(network_resolver).to receive(:getnames).with(unresolved_ip).and_return(resolved_names)
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:unresolved_ip) { '127.0.0.1' }
|
||||||
|
let(:resolved_names) { %w(localhost) }
|
||||||
|
|
||||||
|
it { expect(resolver.names_for(unresolved_ip)).to eq resolved_names }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#ips_for' do
|
||||||
|
context 'when hosts resolver can resolve name' do
|
||||||
|
before :each do
|
||||||
|
expect(hosts_resolver).to receive(:getaddresses).with(unresolved_ips).and_return(resolved_name)
|
||||||
|
if RUBY_VERSION >= '2.1'
|
||||||
|
expect(local_link_resolver).not_to receive(:getaddresses)
|
||||||
|
end
|
||||||
|
expect(network_resolver).not_to receive(:getaddresses)
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:unresolved_ips) { '127.0.0.1' }
|
||||||
|
let(:resolved_name) { %w(localhost) }
|
||||||
|
|
||||||
|
it { expect(resolver.ips_for(unresolved_ips)).to eq resolved_name }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when local link resolver can resolve name' do
|
||||||
|
before :each do
|
||||||
|
expect(hosts_resolver).to receive(:getaddresses).with(unresolved_ips).and_return([])
|
||||||
|
if RUBY_VERSION >= '2.1'
|
||||||
|
expect(local_link_resolver).to receive(:getaddresses).with(unresolved_ips).and_return(resolved_name)
|
||||||
|
expect(network_resolver).not_to receive(:getaddresses)
|
||||||
|
else
|
||||||
|
expect(network_resolver).to receive(:getaddresses).with(unresolved_ips).and_return(resolved_name)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:unresolved_ips) { '127.0.0.1' }
|
||||||
|
let(:resolved_name) { %w(localhost) }
|
||||||
|
|
||||||
|
it { expect(resolver.ips_for(unresolved_ips)).to eq resolved_name }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when network resolver can resolve name' do
|
||||||
|
before :each do
|
||||||
|
expect(hosts_resolver).to receive(:getaddresses).with(unresolved_ips).and_return([])
|
||||||
|
if RUBY_VERSION >= '2.1'
|
||||||
|
expect(local_link_resolver).to receive(:getaddresses).with(unresolved_ips).and_return([])
|
||||||
|
end
|
||||||
|
expect(network_resolver).to receive(:getaddresses).with(unresolved_ips).and_return(resolved_name)
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:unresolved_ips) { '127.0.0.1' }
|
||||||
|
let(:resolved_name) { %w(localhost) }
|
||||||
|
|
||||||
|
it { expect(resolver.ips_for(unresolved_ips)).to eq resolved_name }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,39 @@
|
||||||
|
require 'spec_helper'
|
||||||
|
require 'middleman-core/preview_server/server_hostname'
|
||||||
|
|
||||||
|
RSpec.describe Middleman::PreviewServer::ServerHostname do
|
||||||
|
subject(:hostname) { described_class.new(string) }
|
||||||
|
let(:string) { 'www.example.com' }
|
||||||
|
|
||||||
|
describe '#to_s' do
|
||||||
|
context 'when hostname' do
|
||||||
|
it { expect(hostname.to_s).to eq string }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when ipv4' do
|
||||||
|
let(:string) { '127.0.0.1' }
|
||||||
|
it { expect(hostname.to_s).to eq string }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when ipv6' do
|
||||||
|
let(:string) { '2607:f700:8000:12e:b3d9:1cba:b52:aa1b' }
|
||||||
|
it { expect(hostname.to_s).to eq string }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#to_browser' do
|
||||||
|
context 'when hostname' do
|
||||||
|
it { expect(hostname.to_browser).to eq string }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when ipv4' do
|
||||||
|
let(:string) { '127.0.0.1' }
|
||||||
|
it { expect(hostname.to_browser).to eq string }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when ipv6' do
|
||||||
|
let(:string) { '::1' }
|
||||||
|
it { expect(hostname.to_browser).to eq string }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,43 @@
|
||||||
|
require 'spec_helper'
|
||||||
|
require 'middleman-core/preview_server/server_ip_address'
|
||||||
|
|
||||||
|
RSpec.describe Middleman::PreviewServer::ServerIpAddress do
|
||||||
|
subject(:ip_address) { described_class.new(string) }
|
||||||
|
let(:string) { '127.0.0.1' }
|
||||||
|
|
||||||
|
describe '#to_s' do
|
||||||
|
context 'when ipv4' do
|
||||||
|
let(:string) { '127.0.0.1' }
|
||||||
|
it { expect(ip_address.to_s).to eq string }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when ipv6' do
|
||||||
|
context 'without suffix' do
|
||||||
|
let(:string) { '2607:f700:8000:12e:b3d9:1cba:b52:aa1b' }
|
||||||
|
it { expect(ip_address.to_s).to eq string }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with suffix' do
|
||||||
|
let(:string) { '2607:f700:8000:12e:b3d9:1cba:b52:aa1b%wlp1s0' }
|
||||||
|
let(:result) { '2607:f700:8000:12e:b3d9:1cba:b52:aa1b' }
|
||||||
|
it { expect(ip_address.to_s).to eq result }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#to_browser' do
|
||||||
|
context 'when ip_address' do
|
||||||
|
it { expect(ip_address.to_browser).to eq string }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when ipv4' do
|
||||||
|
let(:string) { '127.0.0.1' }
|
||||||
|
it { expect(ip_address.to_browser).to eq string }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when ipv6' do
|
||||||
|
let(:string) { '2607:f700:8000:12e:b3d9:1cba:b52:aa1b' }
|
||||||
|
it { expect(ip_address.to_browser).to eq "[#{string}]" }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -3,3 +3,29 @@ SimpleCov.root(File.expand_path(File.dirname(__FILE__) + '/..'))
|
||||||
|
|
||||||
require 'coveralls'
|
require 'coveralls'
|
||||||
Coveralls.wear!
|
Coveralls.wear!
|
||||||
|
|
||||||
|
require 'aruba/api'
|
||||||
|
RSpec.configure do |config|
|
||||||
|
config.include Aruba::Api
|
||||||
|
end
|
||||||
|
|
||||||
|
# encoding: utf-8
|
||||||
|
RSpec.configure do |config|
|
||||||
|
config.filter_run :focus
|
||||||
|
config.run_all_when_everything_filtered = true
|
||||||
|
|
||||||
|
config.default_formatter = 'doc' if config.files_to_run.one?
|
||||||
|
|
||||||
|
# config.profile_examples = 10
|
||||||
|
config.order = :random
|
||||||
|
Kernel.srand config.seed
|
||||||
|
|
||||||
|
config.expect_with :rspec do |expectations|
|
||||||
|
expectations.syntax = :expect
|
||||||
|
end
|
||||||
|
|
||||||
|
config.mock_with :rspec do |mocks|
|
||||||
|
mocks.syntax = :expect
|
||||||
|
mocks.verify_partial_doubles = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
Loading…
Reference in a new issue