W3C sperrt Java aus
Heute habe ich bei der Arbeit mit einer XSLT-Engine an meinem Verstand gezweifelt. Die Aufgabe war einfach: Ein kleines Verarbeitungsskript für eine Webseite. Doch leider brach die Verarbeitung immer ab, weil der XSLT-Interpreter die DTD nicht vom Server des W3C laden konnte. Natürlich sollte man besser eine lokale DTD verwenden, dennoch war es überraschend, dass die DTD nicht zugreifbar war. Dem wollte ich auf den Grund gehen, was zu einer zweistündigen Recherche führte. Hier die Ergebnisse in Kurzform.
Der Kopf der Webseite besitzt die übliche Form. Nach der XML-Deklaration folgt die Angabe des Doctypes:
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"
Die in Java geschriebene XSLT-Software bricht die Verarbeitung mit der folgenden Meldung ab.
Error java.io.IOException: Server returned \ HTTP response code: 503 for URL: http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd
Der Zugriff mit dem Browser funktioniert hingegen einwandfrei. Gleiches gilt für den Low-level-Zugriff per Kommandozeilen-telnet auf Port 80.
Kein Bug in HttpUrlConnection
Als Ursache habe ich zunächst einen Bug in Javas HttpUrlConnection vermutet. Greift man nämlich per HTTP, aber ohne Angabe des “Host”-HTTP-Header auf die genannte Adresse zu, erhält man in der Tat eine 503-Antwort. Ein Blick mit Wireshark hinter die Kulissen zeigte aber, dass Java eine vollkommen korrekte HTTP-Anfrage stellt. Aber, Java gibt sich selbst als Client zu erkennen. Das brachte mich auf die Idee, die HTTP-Anfrage manuell nachzuahmen.
Zunächst eine Anfrage ohne Angabe des Clients (um irrelevante Zeilen verkürzte Form):
> telnet www.w3.org 80 Trying 128.30.52.168... Connected to www.w3.org. HEAD /TR/xhtml1/DTD/xhtml1-strict.dtd HTTP/1.0 Host: www.w3.org HTTP/1.1 200 OK Content-Location: xhtml1-strict.dtd.raw Content-Type: application/xml-dtd; charset=utf-8
Dieses Protokoll zeigt, dass die gewünschte Ressource lieferbar ist (Statuscode 200). Jetzt das gleiche, nur gebe ich mich diesmal als Java-Client zu erkennen (um die volle Nachricht zu sehen, verwende ich hier GET statt HEAD):
> telnet www.w3.org 80 Trying 128.30.52.38... Connected to www.w3.org. GET /TR/xhtml1/DTD/xhtml1-strict.dtd HTTP/1.0 Host: www.w3.org User-Agent: Java/1.6.0_16 HTTP/1.1 503 Service Unavailable see http://w3.org/brief/MTE2 X-Abuse-Defenses: see http://w3.org/brief/MTE2 Content-Type: text/plain
see http://w3.org/brief/MTE2Connection closed by foreign host.
Hier bekommt man nun eine Fehlermeldung (503) mit dem Hinweis auf weitere Erklärungen unter den der Adressen http://w3.org/brief/MTE2 und http://w3.org/brief/MTE2Connection. Die zweite kann man (Stand heute) ignorieren. Dort ist nur zu lesen, dass das gewünschte Dokument verschoben wurde — und zwar an die gleiche Adresse(sic!). Unter der ersten Adresse, die auf einen Blogbeitrag vom W3C weiterleitet, erfährt man die Hintergründe.
(Bemerkung: Der gestrichene Text war fehlerhaft; siehe Kommentar von Robert. Danke für den Hinweis.)
Zuviel Traffic
Das W3C leidet unter einer sehr großen Zahl von Zugriffen auf die DTDs. Aus diesem Grund hat das W3C den Zugriff für Programme, die offensichtlich kein Browser sind, geblockt. Die Zugriffszahlen sind in der Tat beeindruckend: 130.000.000 Zugriffe pro Tag!
Der Blogbeitrag sagt weiter aus, dass es sich bei den fraglichen Dingen nicht um Hyperlinks, sondern um URIs handele, die nicht für den Zugriff, sondern nur für die Identifikation bestimmt seien. Soweit es den Namensraum-URI für XHTML angeht, der im W3C-Text genannt wird, stimme ich zu. In meinem oben beschriebenen Fall stimmt das aber nicht. Die Angabe der DTD in der Doctype-Deklaration besteht aus zwei Teilen: einem Public-Identifier und einem System-Identifier. SGML/XML macht keine Aussage, worum es sich bei dem “System” handelt. In der Vergangenheit vor XML war es meist das Dateisystem. Das W3C schreibt den System-Identifier gerne als URI und verwendet damit das Web als “System”. Der System-Identifier ist meiner Meinung nach ausdrücklich für den Zugriff, nicht für die Benennung der DTD (und anderer Dinge) gedacht. Für die Benennung gibt es den Public-Identifier.
Ich hielt es immer und halte es noch für einen Fehler, pauschal URIs als System-Identifier zu verwenden. Sie müssen tatsächlich zugreifbar (also ein URL, nicht nur ein URI) sein, was in vielen Fällen technisch nicht sinnvoll ist. Das Beispiel der öffentlichen XHTML-DTD ist nur eines. Auch im hausinternen Einsatz ist es in der Regel nicht wünschenswert, dass XML-verarbeitende Programme ständig per HTTP auf eine auf einem anderen Rechner gespeicherte DTD zugreifen. Wenn das W3C nun sagt, es handele sich bei dem URI nur um einen Bezeichner, stellt sich automatisch die Frage nach dem Sinn des Public-Identifiers.
Kataloge sind nicht bekannt genug
Das W3C hat es versäumt, mit der Veröffentlichung von XML im Jahre 1998 die längst bewährten Kataloge bekannt zu machen. Sie erlauben ein Mapping von einem Public- (oder System-)Identifier auf einen System-Identifier, z.B. einen Dateinamen. Wir bei Linkwerk promoten Kataloge seit jeher bei jedem Kunden, für den wir XML-Dienstleistungen durchführen. Siehe dazu auch xmllint und Kataloge. Kürzlich habe ich einen Blogbeitrag von Michael Sperberg-McQueen gelesen, in dem er “XML catalogs vs local caching proxy” diskutiert. Der “local caching proxy” ist zweifellos ein interessanter Ansatz, im Falle der W3C-Sperre würde er aber nicht helfen, weil die DTD erst gar nicht in den Cache gelangen würde.
Bleibt noch kurz zu klären, wie ich mein Problem gelöst habe: Natürlich mit einem Katalog, den wir sowieso schon verwenden. Im Falle der Java-Software musste ich noch Hand anlegen. Der Grund ist der, dass der verwendete Resolver nur mit XML-Katalogen, nicht aber mit SGML-Katalogen umgehen kann. Das W3C stellt aber für seine XHTML-1.0-DTDs nur SGML-Kataloge zur Verfügung. Also musste ich mir schnell einen eigenen XML-Katalog basteln. Da er durchaus für andere interessant sein kann, füge ich ihn hier ein.
XML catalog für XHTML 1.0
<catalog xmlns="urn:oasis:names:tc:entity:xmlns:xml:catalog"> <!-- XML catalog for XHTML 1.0 DTDs. Download XHTML 1.0 archive from http://www.w3.org/TR/xhtml1/#dtds and place this file in the DTD subdirectory. Public Domain, Linkwerk.com 2009--> <public publicId="-//W3C//DTD XHTML 1.0 Strict//EN" uri="xhtml1-strict.dtd"/> <public publicId="-//W3C//DTD XHTML 1.0 Transitional//EN" uri="xhtml1-transitional.dtd"/> <public publicId="-//W3C//DTD XHTML 1.0 Frameset//EN" uri="xhtml1-frameset.dtd"/> <public publicId="-//W3C//ENTITIES Latin 1 for XHTML//EN" uri="xhtml-lat1.ent"/> <public publicId="-//W3C//ENTITIES Symbols for XHTML//EN" uri="xhtml-symbol.ent"/> <public publicId="-//W3C//ENTITIES Special for XHTML//EN" uri="xhtml-special.ent"/> </catalog>
November 20th, 2009 at 20:04
Ausgezeichnete Beschreibung der Problematik. Bravo!
Ich sehe allerdings die Frage von Zugriff / Identifikation etwas anderes: ob zugegriffen werden soll oder muss, hängt m.E. weniger von der Art des Bezeichners als von der Art des Verweises ab.
Die Anwesenheit einer Dokumenttypvereinbarung hat eine rein deklarative Bedeutung, sagt etwas über das Dokument aus, und bildet in sich noch keine Anweisung an die Software, das Dokument gegen die DTD zu validieren. (Das ist für Webbrowser wesentlich, und ich verstehe die Äusserungen des W3C in diesem Sinne.) Auch wenn man validieren will, hat der externe Verweis eine deklarative Bedeutung, der man u.U. ohne erneuten Zugriff auf den Server genügen kann (z.B. mittels eines Katalogs oder eines Caches).
Dass man auf den Server gelegentlich zugreifen muss und soll, will auch das W3C nicht bestreiten. Aber wenn bei einem Programm etwas schiefgeht, und das Programm auf dieselbe Ressource wiederholt zugreift, ist es sehr nützlich, eine User-Agent-Angabe zu haben, die das Programm identifiziert. Die Erfahrung hat gelehrt, dass nachzufragen, wer an einem bestimmten Netzknote gestern mit ‘Java/1.6.0_16’ gearbeitet, nichts bringt, denn alle haben das. Die XSLT-Engine, die Sie anwenden, täte besser, sich selbst als User-Agent zu nennen. (Man hat mir gesagt, das sei bei den geläufigen Java Libraries nicht schwierig; ich habe es noch nicht selber probiert.)
November 22nd, 2009 at 13:43
Vielen Dank für die Erwiderung; dazu noch von derart fachkundiger Seite! Das gibt mir Gelegenheit, einige Dinge zu präzisieren, die ich im Blogartikel nicht klar genug formuliert habe.
Ich verstehe den System Identifier auch nicht als Anweisung zur Validierung. Allerdings ist in der XML-Spezifikation an der fraglichen Stelle (http://www.w3.org/TR/REC-xml/#NT-ExternalID) schon deutlich die Rede vom Zugriff auf die Ressource: “Attempts to retrieve the resource identified by a URI may be redirected at the parser level (for example, in an entity resolver) or below (at the protocol level, for example, via an HTTP Location: header).” Das ist für mich ein deutlicher Unterschied zu z.B. Namensraum-URIs, die keine Ressource bezeichnen. Zusätzlich zu meinem Verständnis der Angelegenheit bei SGML, hat diese Stelle der XML-Spezifikation meine Vorstellung geprägt, der System Identifier diene explizit dem Zugriff.
Darüber hinaus verstehe ich voll und ganz, dass und weshalb das W3C den Zugriff wie im Artikel beschrieben, blockiert. Nebenbei: Der provokante Titel war natürlich Absicht, um die Aufmerksamkeit der Leser zu wecken.
Und abschließend: Ich stimme voll damit überein, dass es Aufgabe der XSLT-Engine wäre, sich mit einer vernünftigen User-Agent-Kennung auszuweisen. (Es handelte sich bei meinen Versuchen übrigens um einen nicht ganz aktuellen Saxon 9.x in Zusammenarbeit mit dem “Apache Commons”-Resolver; ich gehe davon aus, dass der Resolver für die unpräzise User-Agent-Angabe verantwortlich ist, da ich den Saxon mit der Option -x:org.apache.xml.resolver.tools.ResolvingXMLReader aufgerufen habe.)
Ich hoffe, dass auch andere Entwickler auf dieses Problem stoßen und sie Lösungen suchen, die den meiner Meinung nach sowieso unnötigen Netzzugriff umgehen; vielleicht ein Katalog oder der “caching proxy”…
August 1st, 2010 at 00:57
Hinweis: In der Antwort des Servers ist die Adresse “http://w3.org/brief/MTE2Connection” nicht enthalten. Die Textnachricht des Servers lautet “see http://w3.org/brief/MTE2“. “Connection closed by foreign host.” wird von telnet ausgegeben, sobald der Server die Verbindung beendet.
edit: Ich habe Google für dieses Posting JavaScript gestatten müssen. Und das obwohl auch Google Java User-Agents aussperrt. Das Captchaelement ist aber ansonsten unsichtbar. ^^
August 1st, 2010 at 15:43
Robert, vielen Dank für den Hinweis. In der Tat habe ich nicht aufgepasst und nicht bemerkt, dass vor dem “Connection closed….” einfach ein Zeilenumbruch fehlt.
Und Ja, das reCaptcha erfordert Javascript. Es gibt auch eine Javascript-freie Variante, die aber umständlicher zu benutzen ist (siehe http://www.heise.de/ix/artikel/Turing-fuer-alle-1033340.html); aus diesem Grund verwenden wir sie für unser Blog nicht. Mir ist klar, dass es mit dem Captcha nicht ganz bequem ist, allerdings hatten wir aufgrund der Spammenge die Kommentarfunktion zuvor komplett deaktiviert — auch keine schöne Option.
April 4th, 2014 at 13:51
Da ich wirklich versuche immer im W3C zu programmieren war mir Java schon immer ein Dorn im Auge deswegen wundert mich dieses überhaupt nicht. Wird den von euch viel mit Java geschrieben oder ist das so wie bei mir eher der Ausnahmezustand