You are here

Weblog von Marix

Wie man in node.js URLs auf Funktionen abbildet

Das "Hallo, Welt!" in node.js ist es einen kleinen Webserver zu bauen der eben diesen Text zurück gibt. Doch was, wenn man nicht nur eine Seite zurückgeben möchte? Eine kleine Funktion hilft abhängig von der eingegebenen URL verschiedenen JavaScript-Funktionen aufzurufen.

Das "Hallo, Welt!" in node.js ist es einen kleinen Webserver zu bauen der eben diesen Text zurück gibt. Doch was, wenn man nicht nur eine Seite zurückgeben möchte? Eine kleine Funktion hilft abhängig von der eingegebenen URL verschiedenen JavaScript-Funktionen aufzurufen. In der Doku zu node.js geht hervor, dass das Request-Objekt die URL im Attribut url enthält. Der einfachste Weg URLs auf Funktionen abzubilden ist ein Objekt zu erstellen welches einfach die Pfade als Attributsname und die dazugehörigen Funktionen als Attributswerte verwendet. Dann können wir einfach direkt am Objekt die URL aufrufen. Das abbildende Objekt könnte so aussehen: var url_mapping = {  '/': printIndex,  '/index.html': printIndex} Die Funktion printIndex kann so aussehen: // Ok, not really an index, but works.var printIndex = function(req, res) {  res.writeHead(200, {'Content-Type': 'text/plain'});  res.end('Hello, World!\n');} Die Abbildung wird dann wie folgt implementiert: mapped = url_mappings[req.url];if (mapped) {  mapped(req, res);} else {  console.log(req.url + ' has not been mapped');  invalidURL(req, res);} Der Code nutzt aus, dass wir in JavaScript dynamisch auf Werte eines Objektes zugreifen können und undefined zurückbekommen wenn der Wert im Objekt nicht gesetzt ist. Theoretisch könnte es hier ein Problem geben da JavaScript Objekte immer ein paar eigene Attribute besitzen, wie z.B. die Funktion hasOwnProperty, aber da die URL immer mit einem Schrägstrich beginnt, kann man mit keinem dieser Werte kollidieren, es sei denn irgendeine JavaScript-Bibliothek beginnt irgendwann wahnsinnigerweise Objekte mit solchen Werten anzureichern. Mit der einfachen Funktion können wir zwar schon URLs auf Funktionen abbilden, oft möchte man aber eine ganze Klasse von URLs auf eine Funktion abbilden. Mithilfe des URL-Moduls könnte man zwar die URL noch um den Query-Teil bereinigen, aber wenn man alle URLs die mit einem bestimmten String anfangen, oder gar die einem bestimmten regulären Ausdruck entsprechen, abgebildet werden sollen kommen wir mit dem bisherigen Ansatz nicht weiter. Um auch diesen Fall abzuhandeln können wir einen Array nutzen der Objekte enthält die jeweils ein Muster auf eine Funktion abbildet: var pattern_mapping = [  { pattern: '/echo/', mapped: echo },  { pattern: /^\/regexp/, mapped: printMatch }]; Möchten wir die richtige Funktion für eine URL finden so müssen alle Muster im Array durchprobieren. Da JavaScripts typeof-Operator leider reguläre Ausdrücke nicht eindeutig identifiziert akzeptieren wir alles welches die für unseren Test benötigte Methode aufweist: for (i = 0; !mapped && i < pattern_mappings.length; i = i + 1) {  pattern = pattern_mappings[i].pattern;  console.log('Testing pattern ' + i + ': ' + pattern + ' [' + typeof(pattern) + ']');  if( typeof(pattern) === 'string' && req.url.slice(0, pattern.length) === pattern ) {    mapped = pattern_mappings[i].mapped;  } else if ( pattern.test && typeof(pattern.test) === 'function' && pattern.test(req.url) ) {    // we can only assume it's a regexp, as typeof is not clear on it,    // but if it has a test function...    mapped = pattern_mappings[i].mapped;  }} Das ganze kann man natürlich noch beliebig aufbohren. Eine naheliegende Verbesserung wäre der aufgerufenen Funktion noch als dritten Parameter das abgebildete Muster zu übergeben. Ohne weitere Verbesserungen sieht die komplette Funktion so aus: function(req, res, url_mappings, pattern_mappings) {  // Forward decleration of variables as recommended by Crockford  var i;  var mapped;   console.log('Request url: ' + req.url);   // check whether the url has a direct mapping  mapped = url_mappings[req.url];  if (mapped) {    mapped(req, res);  } else {    // url did not match any of the direct mappingss    // check whether it matches anny of the patterns    for (i = 0; !mapped && i < pattern_mappings.length; i = i + 1) {      pattern = pattern_mappings[i].pattern;      console.log('Testing pattern ' + i + ': ' + pattern + ' [' + typeof(pattern) + ']');      if( typeof(pattern) === 'string' && req.url.slice(0, pattern.length) === pattern ) {        mapped = pattern_mappings[i].mapped;      } else if ( pattern.test && typeof(pattern.test) === 'function' && pattern.test(req.url) ) {        // we can only assume it's a regexp, as typeof is not clear on it,        // but if it has a test function...        mapped = pattern_mappings[i].mapped;      }    }    if (mapped) {      mapped(req, res);    } else {      console.log(req.url + ' has not been mapped');      invalidURL(req, res);    }  }} Zum einfacheren rumspielen mit dem Code gibt es das Beispiel als Datei: path.js_.txt

Mediatomb vs. PS3 vs. Opera

Seit ich meine PS3 von meinem Heimserver aus mit Videos, Musik und Bildern versorge hatte ich immer wieder das Problem, dass die PS3 scheinbar zufällig das dafür genutzte Mediatomb mal sah, und mal nicht sah. Inzwischen konnte ich das Problem auf eine Wechselwirkung mit Opera Unite zurückführen. Schaltet man diese Funktionalität auf allen Rechnern im LAN aus verschwindet das Problem.

Zwei Hinweise zum IPv6-Routing mit openSUSE

Den Artikel Doppelmoppel – IPv6-Zugang fürs LAN nachrüsten in der aktuellen c't möchte ich nutzen um auf zwei Punkte hinzuweisen welche in den meisten Dokumentationen zum Thema leider fehlen, ohne die es unter openSUSE aber nicht funktioniert.

Den Artikel Doppelmoppel – IPv6-Zugang fürs LAN nachrüsten in der aktuellen c't möchte ich nutzen um auf zwei Punkte hinzuweisen welche in den meisten Dokumentationen zum Thema leider fehlen, ohne die es unter openSUSE aber nicht funktioniert. Das mit dem LAN verbundene Netzwerkinterface benötigt eine gültige IPv6-Adresse aus dem LAN-Subnetz. Diese muss von Hand vergeben werden, da die Zuweisung per radvd am lokalen Interface nicht funktioniert. Diese Adresse kann man via ifconfig add &lt;in radvd eingetragenes prefix&gt;:&lt;mac addresse&gt;/64 vergeben. Ist die Link-lokale Addresse z.B. fe80::442b:39ff:f231:95cd/64 und das Prefix 2a01:498:4c8::/64 so wäre die Addresse 2a01:498:4c8:0:4a5b:39ff:feed:95cd/64. Hat das lokale Interface keine gültige IPv6 im LAN-Subnetz werden die aus dem LAN kommenden Pakete vom Router verworfen. Nutzt man die SuSEfirewall2 muss man diese so konfigurieren, dass sie Verbindungen vom LAN ins IPv6 zulässt. Per Default blockt die SuSEfirewall2 nämlich alle durchzuleitenden IPv6-Pakete. Hierzu editiert man die Datei /etc/sysconfig/SuSEfirewall2 und ändert den Wert FW_FORWARD auf "&lt;lokales IPv6-Netz&gt;,2000::/3". 2000::/3 bezeichnet alle gültigen IPv6-Adressen. Im obigen Beispiel wäre der Eintrag dann FW_FORWARD="2a01:498:4c8::/64,2000::/3". Wichtig bei diesem Eintrag ist die Reihenfolge, denn Verbindungen sind immer vom ersten zum zweiten möglich. Wer die Einträge vertauscht erlaubt also dem ganzen Internet Zugriff aufs LAN, kommt aber nicht raus. Zum Abschluss möchte ich noch auf ein allgemeines Problem bei IPv6 hinweisen. Ein Teil der eigenen IPv6-Adresse wird aus der MAC-Adresse des eigenen Rechners abgeleitet. Dadurch ist man natürlich dauerhaft im Netz identifizierbar. Um das dadurch entstehende Datenschutzproblem zu entschärfen gibt es die IPv6 Privacy Extension, deren Aktivierung ich jedem ans Herz legen möchte. Wie das geht beschreibt ein ausführlicher Artikel bei Heise-Netze für alle Betriebssysteme. Ob die Aktivierung der Privacy Extensions erfolgreich war kann man z.B. durch den Aufruf von sixxs.net prüfen, welches oben rechts die aufrufende IPv6-Adresse anzeigt.

Civilization V im LAN

Nachdem Civilization V ja an Steam gebunden, und damit mit einem gewissen Online-Zwang aber auch SteamPlay, herauskam stellte sich die Frage wie es sich mit dem LAN-Modus verhält. Glücklicherweise bietet Steam ja auch einen Offline-Modus, so dass die LAN nicht unbedingt mit einem Internetanschluss versehen sein muss.

Nachdem Civilization V ja an Steam gebunden, und damit mit einem gewissen Online-Zwang aber auch SteamPlay, herauskam stellte sich die Frage wie es sich mit dem LAN-Modus verhält. Glücklicherweise bietet Steam ja auch einen Offline-Modus, so dass die LAN nicht unbedingt mit einem Internetanschluss versehen sein muss. Ich habe den LAN-Modus bis jetzt übrigens nur in technischer Hinsicht einige Runden, alleine an zwei Rechnern, getestet. Die Startpositionen wirkten überaus fair, aber zum langfristigen Mehrspieler-Spielspaß kann ich so natürlich keine Aussage machen. Fair-Use im LAN Erfreulicherweise ist es möglich Civ V im LAN mit nur einer Lizens auf mehreren Rechnern zu spielen, so wie wir das früher ja eigentlich alle mit Spielen gemacht haben die noch nicht jeder hatte. Die Macher von Steam, die ja als Kopierschutzanbieter vermutlich hinter der entsprechenden Designentscheidung stehen, haben hier wohl den viralen Marketing-Faktor dieser Form von Fair-Use erkannt. Um das Spiel mit einem Steam-Account zu spielen ist wie folgt vorzugehen: Steam auf jedem Rechner mit einem Account der Civ V besitzt starten und in den Offline-Modus versetzen. Es ist wichtig diesen Schritt auf jedem Rechner nacheinander durchzuführen, da ein Steam-Account nur auf einem Rechner gleichzeitig Online sein kann. Hierdurch ist auch sichergestellt, dass die Gäste keine Achievements erreichen können. Den Profilnamen auf den Gästerechnern ändern. Dies ist nicht nur wichtig damit die Spieler im Spiel nicht alle gleich heißen, sondern weil sie sonst auch gar nicht auf den Server kommen. Spiel starten und Spaß haben Am Schluß darf man natürlich nicht vergessen auf den Gästerechnern im Steam wieder den Benutzer zu ändern. Speichern Ein manuelles Speichern ist im LAN nicht vorgesehen. Stattdessen wird das Spiel, anscheinend unabhängig von der Einstellung im Optionsmenü, jede Runde automatisch gespeichert. Man kann zwar mit Strg+S den Speichern-Dialog aufrufen, welcher im Menü fehlt, diese Speicherstände werden aber im Laden-Dialog der beim Spielstart sichtbar ist nicht angezeigt. Da die automatischen Speicherstände nur nach Runde benannt werden empfiehlt es sich bei jeder Spielunterbrechung die aktuelle Runde zu notieren und SteamPlay Auch wenn es dank SteamPlay möglich ist das Spiel, einmal gekauft, sowohl unter Windows als auch auf Mac OS X zu spielen, so klaffen die Versionen doch ein ganzes Stück auseinander. Deshalb ist es leider nicht möglich mit Windows und Mac OS X am gleichen Spiel teilzunehmen. Versucht ein Windows-Rechner auf einen Mac OS X-Server zu verbinden so erscheint eine Meldung, welche auf die abweichenden Versionsnummer hinweist. Ich hoffe hier wird noch einmal darüber nachgedacht den Quelltext der beiden Versionen wieder zusammenzuführen, so dass in Zukunft beide Version synchron sind. Denn auch, dass die zusätzlich herunterladbaren Spielinhalte momentan nur für Windows verfügbar sind finde ich, bedenkt man, dass es sich nur um Völker und Szenarios handelt, unverständlich.

Wenn Apps im Android Market in "Authorizing purchase..." hängen...

scheint dies, kann man Google glauben, ein weit verbreitetes und von Google ignoriertes Problem zu sein. Das Problem ist aber eigentlich leicht, wenn auch mit leichtem Komfortverlust zu lösen.

Seiten

Sollten dir die Artikel auf dieser Seite gefallen und du Bitcoin für ein interessantes Experiment halten, so schicke doch eine kleine Spende an 14pQyjx5EFQCwPBkXMTz5nTcfPsnjHmWqA.

Subscribe to RSS – Weblog von Marix