Um HTML-Dokumente auf ihre W3C-Konformität hin zu überprüfen, ist der offizielle W3C-Validationsdienst häufig die erste Wahl. Eine häufige Benutzung bringt jedoch Probleme mit sich. Daher soll nachfolgend kurz erleutert werden, wie man sich mit Hilfe von Chef einen eigenen Validator installiert und diese mit dem w3c_validators-Gem verwendet.
Motivation
Es ist durchaus wünschenswert, sämtliche HTML-Dokumente auf ihre W3C-Konformität hin zu überprüfen, bevor sie online gestellt werden (warum?). Das kann zum Teil automatisiert (z.B. mit RSpec) oder auch manuell passieren. Zu diesem Zweck gibt es vom W3C-Konsortium den offiziellen Markup Validator.
Hier bei Nix-wie-weg® verwenden wir RSpec und Capybara um alle unsere Seiten automatisch auf valides HTML5 zu testen. Dabei ist es in der Vergangenheit häufig zu Problemen mit Timeouts des offiziellen W3C-Validators gekommen, mit der Folge, dass unsere Tests nicht mehr durchliefen. Außerdem lassen dass die Betreiber dieses Dienstes auch keine unbegrenzte Anzahl von Anfragen eines einzelnen Anschlusses zu.
Nachdem funktionierende Tests ein integraler Bestandteil unseres Workflows sind, war es dringend notwendig, eine bessere Lösung zu finden: Wir haben unseren eigenen Validations-Dienst aufgesetzt.
Installation eines eigenes Validierungs-Webdienstes
Kontext
Der Validator, wie er z.B. auch auf der offiziellen Webseite verwendet wird, besteht eigentlich aus zwei Diensten: einem CGI-Skript, das die Validierung von HTML bis einschließlich Version 4 übernimmt und einem separaten Dienst Validator.nu. Letzterer ist für uns von wesentlichem Interesse, da man damit HTML5 validieren kann. Es handelt sich dabei um eine Java-Anwendung auf Jetty-Basis, die später dann zusätzlich zum Webserver gestartet werden muss.
Vorab sei noch gesagt, dass das Debian-Paket leider hoffnungslos veraltet und deswegen für unsere Zwecke nicht tauglich ist.
Installation mit Chef
Wir werden die Installation anhand eines Chef-Rezepts durchführen. Die Schritte kann man aber natürlich auch manuell durchführen; sie sollten selbsterklärend sein. Wir verwenden hier ausschließlich Debian, aber der Package-Provider von Chef sollte auch mit anderen Distributionen klarkommen. Die Paket-Namen sind dort aber unter Umständen abweichend.
# cookbooks/w3c_validator/recipes/default.rb
required_packages = %w(mercurial subversion libsgmls-perl perl-doc zlib1g zlib1g-dev g++ fcgiwrap)
required_packages.each do |p|
package p
end
Die beiden Dienste sollen nach /usr/local/w3c-validator und /usr/local/validator.nu installiert werden:
# cookbooks/w3c_validator/recipes/default.rb
validator_nu_home = '/usr/local/validator.nu/'
w3c_validator_home = '/usr/local/w3c-validator'
[validator_nu_home, w3c_validator_home].each do |dir|
directory dir do
owner 'www-data'
group 'www-data'
mode 00755
recursive true
end
end
Die aktuelle Version des Validators gibt es auf Github. Chef hat leider bisher noch keinen Deploy-Provider für Mercurial, der das Klonen eines Mercurial-Repositorys für uns übernimmt. Deshalb klonen wir das Repository manuell:
# cookbooks/w3c_validator/recipes/default.rb
execute "clone validator.nu" do
command <<-EOH
hg clone https://bitbucket.org/validator/build build
cd validatur.nu
# Ja, wirklich zweimal
python build/build.py all
python build/build.py all
EOH
user 'www-data'
cwd validator_nu_home
environment ({ 'JAVA_HOME' => '/usr/lib/jvm/java-6-sun' })
creates "#{validator_nu_home}/build"
end
Wir benötigen zunächst noch ein Init-Skript, um den Dienst einfach starten und stoppen zu können.
# cookbooks/w3c-validator/files/default/validator-nu-rc
# Debian Init-Skript für Validator.nu
### BEGIN INIT INFO
# Provides: validator-nu
# Required-Start: $remote_fs
# Required-Stop: $remote_fs
# Should-Start:
# Should-Stop:
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: HTML5 validation service
# Description: HTML5 validation service
### END INIT INFO
. /lib/lsb/init-functions
DIR="/usr/local/validator.nu"
CONTROL_PORT=8887
PIDFILE="/var/run/validator.pid"
DIETIME=10 # Time to wait for the server to die, in seconds
# If this value is set too low you might not
# let some servers to die gracefully and
# 'restart' will not work
# Include defaults if available
if [ -f /etc/default/$NAME ] ; then
. /etc/default/$NAME
fi
DAEMONUSER="www-data"
case $1 in
start)
log_daemon_msg "Starting the process" "$NAME"
if start-stop-daemon --start\
--chuid $DAEMONUSER --chdir $DIR --background\
--exec $DIR/build/build.py -- --control-port=$CONTROL_PORT run
then
log_end_msg 0
else
log_end_msg 1
fi
;;
stop)
log_daemon_msg "Stopping the process" "$NAME"
netcat localhost $CONTROL_PORT
;;
restart)
$0 stop && sleep 2 && $0 start
;;
*)
echo "Usage: $0 {start|stop|restart}"
exit 2
;;
esac
exit 0
# cookbooks/w3c_validator/recipes/default.rb
cookbook_file "/etc/init.d/validator-nu" do
source 'validator-nu-rc'
owner 'root'
group 'root'
mode 00755
end
service 'validator-nu' do
action %w(enable start)
end
Es ist wichtig an dieser Stelle den Parameter –control-port zu setzen, da der Dienst sich sonst nicht in den Hintergrund forkt und sich dazu auch nicht per Start-Stop-Daemon oder nohup dazu überreden lässt. Wir nehmen hier an, dass der Dienst später unter dem Benutzer ‘www-data’ laufen soll.
Es steht noch die Einrichtung des W3C-Validators aus. OpenSP wird als Abhängigkeit benötigt, ist jedoch bei Debian nicht in einer aktuellen Version verfügbar.
# cookbooks/w3c_validator/recipes/default.rb
remote_file "#{w3c_validator_home}/validator.tar.gz" do
source 'http://validator.w3.org/validator.tar.gz'
mode 00644
checksum '741cb3eb7b4e61e57d0caa47114553af2dcecacb7ccc957890df54bcebb464ea'
action :create_if_missing
end
execute 'extract validator' do
command "tar xzf validator.tar.gz"
cwd w3c_validator_home
creates "#{w3c_validator_home}/validator-1.3"
end
remote_file "#{w3c_validator_home}/OpenSP-1.5.2.tar.gz" do
source 'http://downloads.sourceforge.net/project/openjade/opensp/1.5.2/OpenSP-1.5.2.tar.gz'
mode 00644
checksum '57f4898498a368918b0d49c826aa434bb5b703d2c3b169beb348016ab25617ce'
action :create_if_missing
end
execute 'extract OpenSP' do
command 'tar xzf OpenSP-1.5.2.tar.gz'
cwd w3c_validator_home
creates "#{w3c_validator_home}/OpenSP-1.5.2"
end
execute 'compile and install OpenSP' do
command './configure --disable-doc-build && make -j2 && make install'
cwd "#{w3c_validator_home}/OpenSP-1.5.2"
creates "/usr/local/bin/osx"
end
Anschließend muss noch die Konfigurationsdatei an unsere Pfade angepasst werden:
<Paths>
Base = /usr/local/w3c-validator/validator-1.3
Templates = $Base/share/templates
<SGML>
Library = /usr/share/sgml
</SGML>
</Paths>
Allow Debug = yes
Enable Debug = no
Allow Private IPs = yes
Enable SOAP = yes
Max Recursion = 0
<Protocols>
Allow = data,http,https
</Protocols>
Maintainer = webmaster@nix-wie-weg.de
Home Page = http://<%= @vhost_name %>/w3c-markup-validator/
Element Ref URI = http://www.htmlhelp.com/reference/html40/
<Types>
Include types.conf
</Types>
<Charsets>
Include charset.cfg
</Charsets>
<MIME>
text/xml = XML
image/svg = XML
image/svg+xml = XML
application/smil = XML
application/xml = XML
text/html = TBD
text/vnd.wap.wml = XML
application/xhtml+xml = XML
application/mathml+xml = XML
</MIME>
<External>
HTML5 = http://localhost:8888/html5/
</External>
Dateien, die per Include-Direktive eingebunden werden, brauchen nicht geändert zu werden. Der letzte Code-Block ist hier von entscheidender Relevanz: HTML5-Anfragen werden an den HTML5-Validator durchgereicht.
# cookbooks/w3c_validator/recipes/default.rb
w3c_conf = '/usr/local/w3c-validator/validator-1.3/htdocs/config/validator.conf'
template w3c_conf do
source 'validator.conf.erb'
owner 'root'
group 'www-data'
variables({ vhost_name: node['validator']['vhost_name'] })
mode 00644
end
Letzendlich benötigen wir noch mehrere Perl-Module, die wir uns in einer aktuellen Version direkt von CPAN holen:
# cookbooks/w3c_validator/recipes/default.rb
execute 'install CPAN libraries' do
command 'cpan -i Bundle::W3C::Validator XML::LibXML'
end
Nginx-Konfiguration
Bei Nginx ergibt sich das Problem, dass dieser selber keine CGI-Skripte mehr unterstützt. Als Lösung bietet sich hierbei Fcgiwrap an. Dieses Skript bietet eine FastCGI-Schnittstelle zum Webserver und kümmert sich um die korrekte Weitergabe von Fehlern und Umgebungsvariablen.
Installiert haben wir das Paket bereits; sobald der Daemon gestartet ist verläuft die Kommunikation über einen Unix-Domain-Socket (in unserem Fall /var/run/fcgiwrap.socket). Die Konfiguration ist damit einfach:
# /etc/nginx/sites-available/validator.s5.nix-wie-weg.de
server {
listen 80;
server_name <%= @vhost_name %>;
gzip off;
location /check {
root /usr/local/w3c-validator/validator-1.3/httpd/cgi-bin/check;
# Fastcgi socket
fastcgi_pass unix:/var/run/fcgiwrap.socket;
# Fastcgi parameters, include the standard ones
include /etc/nginx/fastcgi_params;
# Adjust non standard parameters (SCRIPT_FILENAME)
fastcgi_param SCRIPT_FILENAME /usr/lib$fastcgi_script_name;
fastcgi_param W3C_VALIDATOR_CFG /usr/local/w3c-validator/validator-1.3/htdocs/config/validator.conf;
}
location / {
ssi on;
root /usr/local/w3c-validator/validator-1.3/htdocs/;
# W3C-Validator Schlampereien reparieren:
rewrite ^/(style/(base|results)) /$1.css break;
}
}
Die Entschprechenden Teile des Chef-Rezepts zum Ablegen der Webserver-Konfigrationsdateien sehen dann so aus:
# cookbooks/w3c_validator/recipes/default.rb
"#{node[:nginx][:dir]}/sites-available/validator.s5.nix-wie-weg.de" do
source 'validator.s5.nix-wie-weg.de.erb'
variables({ vhost_name: node['validator']['vhost_name'] })
notifies :restart, resources(service: 'nginx')
end
nginx_site 'validator.s5.nix-wie-weg.de' do
enable true
notifies :run, :immediately
end
Anwendungsbeispiel mit w3c_validators
Mit dem w3c_validators-Gem kann man nun den eigenen Dienst anstelle des offiziellen verwenden. Das funktioniert deutlich schneller und vor allem zuverlässiger. Wir installieren zunächst das Gem:
$ gem install w3c_validators
Successfully installed w3c_validators-1.2
1 gem installed
Installing ri documentation for w3c_validators-1.2...
Building YARD (yri) index for w3c_validators-1.2...
Installing RDoc documentation for w3c_validators-1.2...
Anschließend kann man irb öffnen und loslegen:
require 'w3c_validators'
w3c = W3CValidators::MarkupValidator.new(
validator_uri: 'http://validator.s5.nix-wie-weg.de/check'
)
w3c.validate_uri('http://www.nix-wie-weg.de')