Umgang mit Passwörtern beim Deployment

Bei der Entwicklung von Applikationen hat man es häufig mit sogenannten “Secrets” zu tun. Das können Passwörter, ConnectionStrings oder API-Tokens sein.

Meistens hat man solche Informationen innerhalb einer Konfigurationsdatei abgelegt und greift zur Laufzeit darauf zu. Das Problem: Checkt man diese Konfiguration ein?

Beim Einchecken dieser Secrets entstehen nun unterschiedliche Probleme:

  • Der Kollege gegenüber nutzt eine andere Testdatenbank und muss somit jedes Mal beim auschecken die entsprechenden Secrets anpassen.
  • Nicht alle im Team die Secrets sehen (private Passwörter)
  • Das Repository extern gehostet (GitHub)
  • Bei einem GIT Deployment auf ein Test/Produktiv-System sollen möglichst die Passwörter ausgetauscht werden

Bei einem Mittagessen habe ich mich mit einem Kollegen intensiv darüber ausgetauscht. Irgendwie ist uns nichts gescheites eingefallen.

Das Deployment Problem ist irgendwie noch lösbar (separate Keys für Prod / Test, Config Transformations in DotNet) aber die Tatsache dass Secrets im Repository liegen ist irgendwie nicht befriedigend.

In den letzten Tagen habe ich mir hier und da ein paar Tools angesehen welche das Problem auf ihre Art und weise zu lösen versuchen.

Jede Lösung hat da seine Besonderheit doch ich glaube man kann alles in einem vier unterschiedliche Vorgehensweisen identifizieren:

 

ENV

Secrets werden als Umgebungsvariablen in die Applikation reingereicht.

In Azure kann man im „Configuration“ Tab seine Secrets hinterlegen. Für DotNet Apps wird nach einem GIT Deployment eine spezielle web.config angelegt auf die man wie gewohnt per ConfigurationManager.AppSettings zugreifen kann.

Dies ist natürlich DotNet spezifisch. Als Fallback werden die Secrets als Umgebungsvariablen in die App reingereicht und können dort abgegriffen werden.

Bsp.: NodeJS:

var mySetting = process.env.MyKey || "Some default value";

Ressourcen:

https://github.com/projectkudu/kudu/wiki/Managing-settings-and-secrets

 

CheckIn-Hoock

Secrets werden vor dem CheckIn verschlüsselt und beim CheckOut entschlüsselt. git-crypt hat sich da gut durchgesetzt.

Ressourcen:

https://github.com/AGWA/git-crypt

 

Secrete-Service

ACHTUNG: Damit ist nicht gemeint dass ihr eure Secrets der US-Regierung zur Verfügung stellen müsst!

Es geht darum, dass ihr einen Dienst nutzt der eure Secrets verwaltet und Ihr diese per API abfragen könnt. Die Abfrage kann dann wiederum durch ein einzelnes Secret gesichert sein oder nur bestimmte IPs oder Nutzer (bsp Azure: Registrierte App in Azure AD) dürfen Anfragen an bestimmte secret-collections stellen.

Ressourcen:

Amazon: https://aws.amazon.com/de/kms/

Selfhosted: http://square.github.io/keywhiz/

Azure: http://blogs.technet.com/b/kv/archive/2015/06/02/azure-key-vault-step-by-step.aspx

 

Config-Volumes

Hier geht es darum secrets in Konfigurationsdateien abzulegen, die Dateien werden aber auf ein separates Volume abgelegt. Die Volumes sind pro Stage (Dev, Test, Prod) unterschiedlich, sind aber unter dem selben Namen auf den entsprechenden Stages verfügbar.

Dieser Einsatz eignet sich sehr gut in einem Docker-Container Umgebung. Dort ist es ein einfaches ein entsprechenden Mount zu definieren und pro Stage diesen abzuändern.

Ressourcen:

https://github.com/asteris-llc/vaultfs

https://github.com/calavera/docker-volume-keywhiz

 

Fazit

Mein Fazit aus der ganzen Sache ist nicht wirklich zufriedenstellen dafür aber einfach: Es kommt drauf an :-)

Betreibe ich eine kleine App in Azure so werde ich wahrscheinlich ein GIT Deployment wählen und es als Konfiguration innerhalb der App ablegen.

Habe ich ein Enterprise Szenario innerhalb von Azure mit vielen Services, so nutze ich wahrscheinlich eher die “azure key vault”

Habe ich ein Microservice Umgebung auf Docker Basis so ist “vaultfs” wahrscheinlich nicht verkehrt.

Habe ich eine Komponente entwickelt welche in bei GitHub veröffentlichen will und brauche dort ein Twitter oder Facebook Token, nutze ich wahrscheinlich “git-crypt”.

Mein Beitrag zum vierten Snippet Wettbewerb

Twitter hat mich auf den vierten Snippet Wettbewern aufmerksam gemacht:

Da habe ich doch direkt mal zwei Snippets eingereicht.

Der erste Beitrag kann einen Mathematischen Ausdruck (im C# Format) ausführen. Die ganze Analyse des Ausdrucks überlasse ich dem C# Compiler. Die Methode generiert eine Klasse, kompiliert diese und führt eine Methode per Reflection aus. Der Rückgabewert wird zurückgegeben.


static double Calculate(string expression){
    var options = new Dictionary<string, string>() { { "CompilerVersion", "v3.5" } };
    var provider = new CSharpCodeProvider();
    
    var dlls = new[] { "mscorlib.dll", "System.Core.dll" };
    var tempFile = Path.GetTempFileName() + ".dll";
    var param = new CompilerParameters(dlls, tempFile, true);
    
    param.GenerateInMemory = true;
    param.GenerateExecutable = false;

    var code =  "using System; " +
                "public class Calculator { " + 
                "  public static double Run() { " + 
                "    return " + expression + "; " + 
                "  } " + 
                "}";

    CompilerResults result = provider.CompileAssemblyFromSource(param, code);

    if(result.Errors.Count > 0 ){
        var errors = string.Join(" -> ", result.Errors.Cast<CompilerError>().Select(e => e.ErrorText));
        throw new ArgumentException("expression is invalid. Compiler Result: " + errors);
    }
    
    var type = result.CompiledAssembly.GetTypes().First(t => t.Name == "Calculator");
    var calcResult = (double)type.GetMethod("Run").Invoke(null, null);

    return calcResult;
}

 

Der zweite Beitrag ermöglicht über dynamic den Zugriff auf private Methoden und Felder. Sinnvoll für UnitTests wenn man mal was mocken und eine private Methode testen möchte. Elementar ist bei dem Snippet die DynamicObject Klasse


public class PrivateAccess : DynamicObject
{
    private readonly object _nested = null;
    public PrivateAccess(object obj)
    {
        _nested = obj;
    }

    public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
    {
        result = null;

        var methods = _nested.GetType().GetMethods(BindingFlags.Instance | BindingFlags.NonPublic);
        var method = methods.FirstOrDefault(m => m.Name == binder.Name && m.GetParameters().Count() == binder.CallInfo.ArgumentCount);
        if (method == null)
        {
            return false;
        }

        result = method.Invoke(_nested, args);
        return true;
    }

    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        result = null;

        var field = _nested.GetType().GetField(binder.Name, BindingFlags.Instance | BindingFlags.NonPublic);
        if (field == null)
        {
            return false;
        }

        result = field.GetValue(_nested);
        return true;
    }

    public override bool TrySetMember(SetMemberBinder binder, object value)
    {
        var field = _nested.GetType().GetField(binder.Name, BindingFlags.Instance | BindingFlags.NonPublic);
        if (field == null)
        {
            return false;
        }

        field.SetValue(_nested, value);
        return true;
    }
}

Ich würde mich über eure Votes freuen :)

Orientierungslos – iOS UIApplicationInvalidInterfaceOrientation

Habe gerade bei der Entwicklung einer iOS App die UIApplicationInvalidInterfaceOrientation Exception bekommen.

Meldung: “Supported orientations has no common orientation with the application, and shouldAutorotate is returning YES”

Das Problem:

Ich möchte in einem bestimmten ViewController nur LandscapeLeft als Orientierung unterstützen. Dementsprechend habe ich supportedInterfaceOrientations überschrieben und liefere dort UIInterfaceOrientation.LandscapeLeft zurück.

Da ist auch schon das Problemkind.
Es wird UIInterfaceOrientationMask.LandscapeLeft erwartet.

In Swift sieht das dann so aus:

#!csharp
override func supportedInterfaceOrientations() -> Int {
    return Int(UIInterfaceOrientationMask.LandscapeLeft.rawValue)
}

Finden statt komprimieren

Heute wieder mal bei πfs  vorbeigeschaut.

Da hat sich einer gedacht: “hey! Warum Daten komprimieren? Die Zahl PI hat doch im Prinzip alle möglichen Datenkonstellationen. Man muss ja nur den Start-Index, wo die Daten anfangen, finden.

Schaut es euch an: πfs: Never worry about data again!

Nett ist auch die Ideen Liste:

  • Variable run length search and lookup!
  • Arithmetic Coding!
  • Parallelizable lookup!
  • Cloud based π lookup!
  • πfs for Hadoop!

Finde es immer wieder cool dass manche Leute so beklopptes Zeug bauen.

OSX mini Activity Monitor [quantuMon]

Electron

Die Tage habe ich etwas mit electron rumgespielt. Damit könnt ihr NodeJS Anwendungen auf den Desktop laufen lassen.

Als Resultat des ganzen Prototypings ist ein kleiner System Monitor für OSX entstanden, der sich in die Menubar integriert.

So siehts dann aus:

OSX menubar system monitor app with the power of Electorn and the beauty Material Design

OSX menubar system monitor app with the power of Electorn and the beauty Material Design

 

Die Quellen und das Binary findet Ihr bei GitHub.

Würde mich über Feedback freuen!

Aus Windows 8.1 mach Windows.old

Nachdem ich mein Windows 8.1 auf Windows 10 aktualisiert habe, liegt ein Ordner namens Windows.old auf der System Platte.

Anscheinend haben die da echt einfach mal eine Sicherung erstellt und die nach dem Update nicht gelöscht. Das ding ist nicht gerade klein!

Auf meinem Macbook nutze ich seit Anfang des Jahres fast ausschließlich das OSX, dennoch habe ich ein Windows parallel nativ installiert. Dank VMWare kann ich sogar die native Installation in einer VM nutzen und so zwischen den Systemen beliebig hin und her wechseln.

Das einzige Problem: Festplattenplatz!

Beide Systeme Brauchen eine eigene Partition. Die Windows Partition ist relativ klein da ich dort keine Daten (nur halt Tools habe). Das doofe Windows.old Verzeichnis frisst bei mir knappe 8 GB. Muss nicht sein.

Dank an den Webninja für die super Anleitung zum entfernen:

Windows.old Ordner nach Windows 10 Update löschen

Buchempfehlung: Silicon Valley – Was aus dem mächtigsten Tal der Welt auf uns zukommt

Ich bin ein begeisterter Audible Kunde. Es ist einfach super bei langen Autofahrten ein Hörbuch anzuschmeißen und zuzuhören.

Silicon Valley: Was aus dem mächtigsten Tal der Welt auf uns zukommt

In den letzten Tagen war das Hörbuch Silicon Valley: Was aus dem mächtigsten Tal der Welt auf uns zukommt dran.

Der Inhalt und auch der Sprecher sind super. Am spannendsten fand ich das Thema Disruptive Innovation. Dazu kommen ein paar Anekdoten von Startups.

Später kommt ein wenig google bashing und (gerechtfertigte) Panikmache was das Freigeben unserer Daten angeht. Spannendes Thema aber nichts neues für mich.

Das Buch gibts natürlich bei Amazon.

Alternativer Color Picker für OSX

Skala Color

Ich war die Woche mal im Xcode unterwegs und musste viel mit Farben rumspielen. Da kam mir Skala Color zur Hilfe. Das Tool integriert sich als ein color picker in das OSX und steht damit in allen Apps zur Verfügung. Neben der einfachen Eingabe von unterschiedlichen Formaten (per Click, HTML HEX, HSL), hat es auch unterschiedliche Formate für die Ausgabe (Copy as Swift NSColor RGBA / HSBA, usw.)

Es gibt auch ein super gettig started welchen ich aber nicht durch die Startseite gefunden habe.

Man muss schon Google bemühen um auf Unterseiten zu kommen kopfschüttel

We need to transmit the 1080p ram bus!

Wenn ich mit einem Handwerker spreche dann komme ich mir oft ziemlich unterbelichtet vor. Irgendwie haben diese Leute die Einstellung dass jeder der mit denen redet auch ihre DSL sprechen muss.

Ich mach das jetzt auch so! Auf Fragen einfach mit Technischen Nonsens antworten.

Die passenden Sprüche liefert mir der www.hacker.actor. Übrigens basiert das Tool auf stympy/faker von dem es unter Marak/faker.js einen JS Fork gibt.

Das Internet gibt mir echt alle Tools um im Real Life ein Troll zu sein.

Automatische Proxy Authentifizierung

Bei einem Kunden habe ich das Problem, dass ich hinter einem Proxy sitze welcher immer eine Authentifizierung erfordert. Das allein ist erstmal vollkommen OK, aber manche Tools wollen nun mal mit dem Internet sprechen und behandeln nicht explizit die NotAuthorized Antwort eines Proxys. Jeder Browser zeigt ein entsprechendes Login Fenster an und man kann sein Internet Passwort eingeben, doch gerade bei den Commandline Tools ists schwierig. Wobei auch da einige tatsächlich die Möglichkeit bieten noch Proxy Credentials mit anzugeben aber eben nicht alle.

Ich nutze gerne den Fiddler als WebDebugger! Da dieses Tool als lokaler Proxy nochmal zwischen jedem Request und Response (was HTTP angeht) hockt, eignet sich ausgezeichnet dafür mein nutzernahen und Passwort einfach pauschal in die Kommunikation zu injizieren :)

So einfach gehts:

(1) Kodiert eurer Nutzernamen und eurer Passwort base64, die Schreibweise müsste nutzername:passwort sein. Zum kodieren könnt ihr den Fiddler nehmen -> Tools -> TextWizard.

(2) Passt die Rule “OnBeforeRequest” an. Findet tut ihr die unter Rules -> Customize Rules -> Runterscrollen bis “OnBeforeRequest” und dort entsprechendes hinzufügen:

if (!oSession.isHTTPS) 
{
  oSession.oRequest["Proxy-Authorization"] = "Basic <<NUTZERNAME:PASSWORT BASE64>>";
}

Den Text << NUTZERNAME:PASSWORT BASE64 >> müsst Ihr durch den in Schritt 1 erstellten base64 kodierten username:password Zeichenstring ersetzen. WICHTIG: KEINE Spatzenklammern!

Solange der Fiddler läuft ist nun keine Eingabe von Nutzername und Passwort für den Proxy erforderlich.

NodeJS auf einem Mac installieren

Ich bin seit neuestem stolzer Besitzer eines Macbook Pro. Hab dann mal direkt NodeJS dort zum laufen gebracht. Leider hatte ich am Anfang mit enormen Problemen zu kämpfen. Andauernd mussten sämtliche npm -global Installationen mit sudo ausgeführt werden, was echt nervig ist. Vor allem wenn man Tools wie yo oder grunt einsetzt. Nach langem hin und her hier nun die Schnellanleitung für “npm and node without sudo”:

(1) Homebrew (Mac OS Package Manager) installieren

#!bash
ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

(2) brew updaten

#!bash    
brew update && brew upgrade

(3) NVM (Node Version Manager) installieren, damit kann man unterschiedliche Versionen von Node auf einem Rechner betreiben

#!bash    
brew install nvm

(4) NVM vom Terminal verfügbar machen (.bashrc durch .profile oder .zshrc ersetzen, jäh nachdem was ihr verwendet)

#!bash    
echo "source $(brew --prefix nvm)/nvm.sh" >> ~/.bashrc

(5) Die neueste Node Version installieren

#!bash    
nvm install stable

Die aktuelle Version als Standard Node Instanz nutzen

#!bash    
nvm alias default <version>

(6) FERTIG!

Gesehen bei: nothingtodisplay.org

Blockies: der Avatar Generator

Bin heute zufällig auf Blockies gestoßen. Die Idee finde ich echt cool und ich denke mal dass ich die Lib demnächst verwenden werde.

Der Aufruf ist verdammt einfach:

#!javascript
var icon = blockies.create({ // All options are optional
    seed: 'randstring', // seed used to generate icon data, default: random
    color: '#dfe', // to manually specify the icon color, default: random
    size: 15, // width/height of the icon in blocks, default: 10
    scale: 3 // width/height of each block in pixels, default: 5
});    
document.body.appendChild(icon); // icon is a canvas element

So kann es dann aussehen:

Blockies Sample

Beziehen könnt Ihr es wie immer bei GitHub

Smooth scrolling im iOS Safari

Nachdem ich das Erste Mal mit dem iPhone diesen Blog aufgerufen habe, war ich etwas erstaunt über das Scroll-Verhalten der Seite.
Irgendwie war es nicht mehr so ganz flüssig und smooth wie sonst immer gewohnt.
Es hat sich herausgestellt dass dieses Verhalten immer nur auf dem body Element der Seite wirkt.
Die Liste der Artikel befinden sich in einem Container, welcher auf overflow: scroll steht. Somit zieht diese Regel nicht mehr.

Abhilfe schafft aber der CSS Style

#!css
.myDivContent {      
  -webkit-overflow-scrolling: touch;
  overflow-y: scroll;
}

Damit scrollt es sich wieder smooth 😉

Way to Node – Chapter 1 – type faster than your shadow

lucky luke

In dem Ersten Post der Reihe möchte ich ein Spiel in NodeJS realisieren. Das Prinzip des Spiels ist recht einfach: Dem Benutzer wird ein Wort angezeigt und er hat 10 Sekunden Zeit es einzugeben. Schafft er es, so folgt das nächste Wort. Schafft er es nicht, so ist das Spiel vorbei. Mit jedem erratenem Wort wird sein Score um eins erhöht.

Das Spielprinzip kann ganz leicht im Browser umgesetzt werden, dazu reicht es eine statische HTML Seite mit etwas JavaScript auszuliefern. Ich möchte mich mit NodeJS beschäftigen und setze deswegen die Spiel Logik auf dem Server um. Der Client holt sich nur eine Statische HTML Seite, aktuelle Wort und den aktuellen Status (gewonnen / verloren).

Ich überspringe hier die ganze Erklärung was NodeJS ist. Dieses Wissen setze ich einfach mal voraus. Wer diese Informationen braucht den verweise ich gerne auf die offizielle NodeJS Seite

So! Genug vom Prosa, kommen wir zum Code:

Als aller erstes solltet ihr einen Ordner mit den folgenden Dateien erstellen:

  • app.js

  • fast_typer.js

  • words.js

  • index.html

Die Dateien können ruhig leer sein, wird werden Sie gleich mit Code füllen.

reguire

Kapselungen von Code sind in allen Sprachen und Frameworks gang und gäbe. Node nutzt dazu das Prinzip der Module. Ein Modul ist eine Sammlung von Funktionen ähnlich einer Klasse mit rein Statischen Methoden. Man kann innerhalb eines Moduls aber Typen definieren und diese ebenfalls zurückgeben, wodurch eine Objektorientierung nachgeahmt werden kann. Werden Funktionalitäten eines Moduls benötigt so muss das Modul geladen werden. Dazu verwendet man die Funktion require.

Wir benötigen zu Anfang direkt fünf Module und binden diese in unsere App.js mal ein:

#!javascript
var fs = require('fs');
var http = require('http');
var url = require("url");
var querystring = require("querystring");

Das fs Modul bietet Funktionen zum Zugriff auf das Dateisystem. Wir werden es benötigen um unsere HTML Datei von der Platte zu lesen.

Das Modul http ist eines der wichtigsten Module wenn es um die Web-Entwicklung mit NodeJS geht. Es liefert uns die Implementierung eines HTTP Servers

Das url Modul bringt eine Ansammlung von Funktionen zum parsen und formatieren von URL-Strings

Zum Schluss noch das querystring Modul, welches ähnlich den url Modul Funktionen zum parsen und formatieren von querystrings (das sind die Zeichen hinter dem Dateinamen in der URL) bietet.

readFile vs readFileSync

Nun merken wir uns den Inhalt unserer index.html in einer Variable, um diese bei jedem Request ausliefern zu können.

#!javascript
var mainPage = fs.readFileSync('index.html');

Die Funktion readFileSync des fs Moduls ist eine Ausnahme in NodeJS. Typischerweise sind alle Funktionen welche auf externe Ressorcen zugreifen (Datenbank, Dateisystem, Netzwerk) asynchron. Die Ausführung des Codes bleibt nicht an der Stelle stehen bis die Datei gelesen wurde, nein es geht erstmal weiter. Wenn dann das File gelesen wurde wird man per Event informiert. Ich habe es mir hier einfach gemacht und die synchrone Variante von readFile verwendet. Der Vollständigkeit halber hier noch der Aufruf in der asynchronen Variante:

#!javascript
var mainPage;
fs.readFile('index.html', function (err, data) {
  mainPage = data;
});
//ACHTUNG: mainPage ist hier noch null! 
//Wird das Ergebniss von readFile benötigt
//so kann erst nach dem Aufruf des Callbacks 
//weiter gemacht werden.

Der Einfachheit halber, nutze ich erstmal die synchrone Variante.

http.createServer

Nun haben wir unsere index.html geladen und müssen diese noch ausliefern. dazu folgendes:

#!javascript
http.createServer(function(req, res){
  res.writeHead(200, {
    'Content-Length': mainPage.length,
    'Content-Type': 'text/html' 
  });

  res.end(mainPage);

}).listen(8080);
console.log('server is running');

Das müsste es gewesen sein. Schreibt etwas HTML Code in eure index.html, startet Node mit:

node app.js

in dem aktuellen Verzeichnis und ruft die Seite, mit dem Browser eurer Wahl, unter localhost:8080 auf. Die Seite wird ausgeliefert und ihr habt mit etwa 15 Zeilen Code einen laufenden Web-Server.

words.js

Wir haben nun gesehen wie man einen HTTP Server erstellt und Abhängigkeiten in Form von Modulen lädt. Nun möchte ich die Spiellogik in ein eigenes Modul verpacken und es später hier in der App zu verwenden. Dieses Modul hat nebenbei ebenfalls eine Abhängigkeit zu einem weiteren selbst entwickelten Modul: words.js.

Wir fangen aber mal mit einem an. Ich habe ein Textfile mit mehr als 5000 Englischen Wörtern. Das Modul words ließt diese Datei und kann liefert ein Wort per Zufallsprinzip.

Diesen Code könnt ihr 1:1 in die words.js Datei kopieren:

#!javascript
var fs = require('fs');

var words = fs.readFileSync('wordfile.txt', 'utf8').split('rn');

function random(from, to){
  return Math.floor((Math.random() * (to-from +1)) + from);
}

function getRandomWord(){
  var idx = random(0, words.length);  
  return words[idx];
}

exports.getRandom = getRandomWord;

Nun zum Aufbau: In der ersten Zeile wird eine Referenz zum fs Modul sich geholt.

Danach wird synchron die Datei wordfile.txt gelesen und nach Zeilenumbrüchen gesplittet. Das Ergebnis, ein String Array, wird sich in der Variablen words gemerkt.

Nun wird eine Funktion namens random deklariert welche eine Zufallszahl zwischen den gegebenen Parametern ermittelt.

Als nächstes steht da noch die Funktion getRandomWord, welche sich eine Zufallszahl ermitteln lässt und diesen als Index für das oben erstellte String Array nutzt.

Zu guter Letzt: exports.getRandom = getRandomWord;

Die exports Variable innerhalb eines Moduls dient dazu Funktionen (auch Variablen) nach Außen zu geben. Alles was an der Variable exports nach der Ausführung des Modul Codes noch ist, ist die Schnittstelle des Moduls. In unserem Fall kann der Aufrufer die Funktion getRandom an dem Modul aufrufen, welche wir mit der Referenz von getRandomWord beschrieben haben.

fast_typer.js

Jetzt wo wir an zufällige Wörter kommen, können wir uns auf die Spiellogik konzentrieren.

Die Logik implementiere ich in einer Art Klasse (wenn man in JS von Klassen reden kann). Dieses gibt mir die Möglichkeit öffentliche und private Member zu simulieren. Mit einem Modul hätte ich dieselben Möglichkeiten, kann aber die Spiel-Logik nur in Verbindung eines Modul-Systems in anderen Anwendungen wiederverwenden. Ich zeige euch mal den Code und gehe auf den Aspekt noch genauer ein:

#!javascript
var words = require('./words');

var FastTyper = function(){

  var _sessions = {};
  var _sessionIdCounter = 0;
  var _loopHandle = null;
  var TIME_LIMIT_SEC = 10;

  function createSessionId(){
    var inc = Math.floor(Math.random() * 42) + 1;
    _sessionIdCounter += inc;
    return _sessionIdCounter + '';
  }
  function getRandomWord(){
    return words.getRandom().toLowerCase();
  }

  function isSessionExpired(session){
    var currentDate = new Date();
    var diff = (currentDate.getTime() - session.wordCreationTimeStamp.getTime()) / 1000;
    return (diff > TIME_LIMIT_SEC);
  }

  function loop(){
    for(var sessionId in _sessions){
      if(!_sessions.hasOwnProperty(sessionId))
        continue;


      if(isSessionExpired(_sessions[sessionId]))
        delete _sessions[sessionId];
    }
  }

  this.startGame = function(usrName){
    var newSession = createSessionId();
    _sessions[newSession] = {
      id : newSession,
      usrName : usrName,
      currentWord : getRandomWord(),
      wordCreationTimeStamp : new Date(),
      score : 0
    };

    return {
      sessionId : _sessions[newSession].id,
      name: usrName,
      score : _sessions[newSession].score,
      word : _sessions[newSession].currentWord,
      remaining: TIME_LIMIT_SEC
    };
  };

  this.solve = function(sessionId, word){

    if(typeof _sessions[sessionId] == 'undefined')
      return {error: 'timeout'};

    var session = _sessions[sessionId];
    if(isSessionExpired(session))
      return {error: 'timeout'};

    if(session.currentWord != word){
      delete _sessions[sessionId];
      return {error: 'failed'};
    }

    session.currentWord = getRandomWord();
    session.wordCreationTimeStamp = new Date();
    session.score++;

    var result = {
      sessionId : session.id,
      name : session.usrName,
      score : session.score,
      word : session.currentWord,
      remaining: TIME_LIMIT_SEC
    };    

    return result;
  };

  this.startLoop = function(){
    _loopHandle = setInterval(loop, 100);
  };

  this.endLoop = function(){
    clearInterval(_loopHandle);   
  }

};

var fastTyper = new FastTyper();
fastTyper.startLoop();

exports.startGame = fastTyper.startGame;
exports.solve = fastTyper.solve;

Gehen wir mal Zeile für Zeile durch.

In Zeile eins wird die Abhängigkeit words geladen und in der Variable words gemerkt. Wundert euch nicht über das ./ am Anfang von dem Modul Namen. Es ist so dass das Laden von Modulen immer einen relativen Pfad voraussetzt. Ausnahmen sind in NodeJS definierte Module oder diejenigen welche man sich per Paketmanager herunterlädt. Ich werde auf den Paketmanager noch in einem anderen Post genauer eingehen.

In Zeile drei wird eine Variable mit dem Namen FastTyper deklariert und der Zeiger auf eine Funktionsdeklaration gesetzt.

In den Zeilen fünf bis sieben werden ein paar private Felder deklariert.

Zeile acht deklariert eine Variable welche ich aber als Konstante nutzen möchte.

Zeile 10 bis 14 deklariert die private Funktion createSessionId. Diese Funktion dient mir als ein Sequenz-Generator. In dem Feld _sessionIdCounter wird die letzte generierte SessionId sich gemerkt, bei einem Aufruf der Funktion createSessionId wird diese um einen Wert zwischen 1 und 42 erhöht. Das Ergebnis, die neue Id, wird zurückgeliefert.

Zeile 15 bis 17 kapselt nochmal die Abhängigkeit words und liefert ein zufälliges Wort, bei welchem aber die Buchstaben klein gerechnet werden.

Zeile 19 bis 23 Deklariert die Funktion isSessionExpired. Diese Funktion erwartet als Parameter ein Session Objekt (wie das aussieht sehen wir noch später). Es wird anhand des Properties wordCreationTimeStamp ermittelt ob es TIME_LIMIT_SEC in der Vergangenheit git. Wenn “JA” liefert die Funktion true zurück, bei “NEIN” so false.

Zeile 25 bis 34 deklariert die Funktion loop. Diese Funktion wird zyklisch aufgerufen, sie geht sämtliche Sessions durch und entfernt abgelaufene.

Eventuell ist euch aufgefallen dass die Variable _sessions ein Objekt und kein Array ist. Nun ja, das Schöne an JS schwachem Typsystem ist, dass es auch erweitert werden kann. Ich nutze das Objekt als eine Art Hashtable/Dictionary. Der Key ist die SessionId, der Value ist das Session Objekt. Würde ich hier mit einem Array arbeiten, so könnte ich auf Objekte nur per Index zugreifen. Ich muss den Zugriff aber per Id ermöglichen. Das Tolle ist aber dass ich ein Objekt wie ein Array durch-iterieren kann. Was ihr in Zeile 26 sehen könnt. Als Wert erhält man bei jedem Durchlauf den Wert eines Properties. Machen wir weiter…

Zeile 36 bis 44 definiert eine öffentliche Funktion dieses Typen: startGame. Als Parameter geht der Nutzername als String herein. Es wird eine neue SessionId erstellt und einfach dem Objekt _sessions ein neues Property (durch die Eckigen Klammern) hinzugefügt. Das Property besteht wiederum aus einem Objekt mit den Eigenschaften

  • id

  • userName

  • currentWord (welches direkt vorgefüllt wird)

  • wordCreationTimeStamp (welches ein aktuellen Zeitstempel enthält)

  • score

Zum Schluss liefert die Funktion noch ein Objekt zurück. Dieses entspricht im groben dem Objekt der Session mit der Ausnahme des Zeitstempels. Dieser fällt weg, dazu kommt aber das property remaining, welches aussagt wie viel Zeit der Spieler zur Eingabe des aktuelle Wortes hat.

In den Zeilen 55 bis 82 ist die Funktion zum Auflösen eines Wortes deklariert. Als Parameter kommt eine SessionId und das zu Lösende Wort. es werden ein paar Prüfungen durchgeführt ob die Session abgelaufen oder das Wort falsch ist. Ist dies der Fall so kommt eine Fehlermeldung zurück.

Sollten die Prüfungen erfolgreich gewesen sein so bleibt die Session am Leben, ein neues Wort wird generiert und der Score wieder hoch gesetzt. Zum Schluss wird das Ergebnis, ein “Response” Objekt, zurückgegeben.

In den Zeilen 84 bis 90 wird die Loop, welche die Sessions aufräumt, gestartet (bzw. beendet).

Die Zeilen 94 und 95 kümmern sich nun darum ein neues Objekt des Typs zu erstellen und direkt zu starten.

Am Ende wird in den Zeilen 97 und 98 die Zwei öffentlichen Funktionen startGame und solve als Modul-Schnittstelle zur Verfügung gestellt.

Das War es auch schon mit der ganzen Logik. Nun, ich könnte mir den ganzen Quatsch mit dem eigenem Typen sparen. Jetzt habe ich aber die Möglichkeit den Code zwischen den Zeilen 10 und 92 z.B. im Browser wiederzuverwenden. Ob ich das tun werde? Wahrscheinlich nicht! ich wollte es mal ausprobieren :).

app.js die Zweite

Kommen wir nun wieder zurück zur unserer App.js. Als erstes sollten wir die Abhängigkeit zu unserem fast_typer Modul laden:

#!javascript
var fastpr = require('./fast_typer');

Diese Zeile sollte möglichst weit oben bei den anderen require Anweisungen stehen.

Danach ersetzen wir mal den ganzen Code, mit dem wir unseren HTTP Server erstellt haben:

#!javascript
http.createServer(function(req, res){
    var rqUrl = url.parse(req.url);  

  if(rqUrl.pathname == '/'){
    res.writeHead(200, {
        'Content-Length': mainPage.length,
        'Content-Type': 'text/html' 
    });

    res.end(mainPage);    
  }
  else if(rqUrl.pathname.indexOf('/solve') == 0){
    var rqQuery = querystring.parse(rqUrl.query);    
    var responseObj = fastpr.solve(rqQuery.sessionId, rqQuery.word);
    sendResponseObject(res, responseObj);

  }
  else if(rqUrl.pathname.indexOf('/start') == 0){
    var rqQuery = querystring.parse(rqUrl.query);        
    var responseObj = fastpr.startGame(rqQuery.name);
    sendResponseObject(res, responseObj);    
  }  
  else{
    var notFoundPage = '<html><head><title>404 - Not found</title></head><body><h1>Not found!</h1></body></html>';

    res.writeHead(404, {
        'Content-Length': notFoundPage.length,
        'Content-Type': 'text/html' 
    });

    res.end(notFoundPage);
    console.log('404 request: ' + rqUrl.pathname);
  }

}).listen(8080);

OK! Gehen wir wieder Zeile für Zeile durch: Am eingehenden Request Objekt (req) wird das Property url ausgelesen und geparsed. Vom dem Ergebnis, einem URL Objekt, können wir den angefragten Pfad abgreifen.

Hat der Nutzer die Hauptseite betreten (http://domain.de/) so wird die mainPage zurückgeliefert.

Wurde hinten ein solve angestellt (http://domain.de/solve) so wird am fast_typer Modul (hier in der Variablen fastpr) die Funktion solve aufgerufen und das Ergebnis zurück geschickt.

Wird die Ressource start angefragt (http://domain.de/start) so wird am fast_typer Modul die Funktion startGame aufgerufen und auch dessen Ergebnis wird zurück geschickt.

Zu guter Letzt wird bei allen anderen Anfragen eine simple 404 Seite ausgeliefert.

Der Vollständigkeit halber hier noch die Funktion senResponseObject:

#!javascript
function sendResponseObject(res, responseObj){
  var result = JSON.stringify(responseObj);

  res.writeHead(200, {
      'Content-Length': result.length,
      'Content-Type': 'application/json' 
  });

  res.end(result);
}

Das Response Objekt wird JSON serialisiert und dann in den Response geschrieben.

Client

Auf den Client Code möchte ich ungern eingehen. Es ist eine Statische HTML-Seite welche per JQuery $.get den Server aufruft und anhand der Ergebnisse etwas UI Modifikationen durchführt. Ihr könnt euch die Seite (und alle anderen Files) weiter untern Downloaden.

Fazit

Ich bin DotNet Entwickler und habe mir das Prinzip “Thread per Request” stark verinnerlicht. Es bedeutet dass bei jeder Anfrage ein Thread aus dem ThreadPool genommen wird und der Request innerhalb dieses Threads ausgeführt wird. Kommen zwei Request parallel am Server an, so werden diese parallel abgearbeitet.

Diese parallele Abarbeitung setzt aber eine Thread Sicherheit vorraus. Man sollte in Web-Anwendungen unbedingt auf geteilte (static) Variablen verzichten.

Wenn ihr euch nun das Modul fast_typer nochmal anschaut so werdet ihr feststellen dass ich mir darüber keine Gedanken mache. Das liegt daran, dass Node alle Requests in einem Thread abarbeitet. Es ist alles Synchronisiert. Sobald I/O Zugriffe durchgeführt werden, werden diese Asynchron durchgeführt. Der EINE Request Thread kümmert sich währenddessen um die Abarbeitung des nächsten Requests.

Ich muss gestehen dass ich beim Schreiben des fast_typer Moduls immer wieder mir das in Erinnerung rufen musste. Andauernd wollte ich irgendwo ein lock einbauen. Ich bin halt DotNet vorbelastet :) .

Irgendwie ist diese Art der Entwicklung recht erfrischend. Ich bin mir noch nicht sicher ob es wirklich gut oder schlecht ist. Es ist halt anders und fühlt sich erstmal GUT an.

Richtige Web Anwendungen mit purem Node will ich aber nicht entwickeln. Alleine das Routing ist hier ein Kampf. Da hilft einem ein schönes Framework garantiert aus. Dazu aber in einem andrem Post.

PS:

Eine Bitte habe ich noch an euch: Das ist mein erster Artikel in Form eines Tutorials! Ich hätte gerne Feedback wie ihr es findet. Ist es zu viel Text? Ist es nicht vernünftig erklärt?

Ich denke die nachfolgenden Artikel werden weniger Code enthalten. Den Code stelle ich aber zum Download bereit!

Hier habt ihr noch das ganze zum Download:

type_faster_than_your_shadow.zip

Außerdem könnt ihr das Spiel auch selbst mal ausprobieren:

PLAY

Way to Node – Prolog

Wie jeder Entwickler habe auch ich Spaß daran mit Technologien zu Experimentieren. Aktuell hat es mir NodeJS angetan, morgen wird es vielleicht was anderes.

node_logo Ich glaube dass man die Vorteile einer Technologie nicht “nur” durch lesen von Artikeln und ansehen von Präsentationen erkennt. Natürlich gilt dies ebenfalls für die Nachteile. Nun ja ich spiele also gerade etwas mit NodeJS herum und möchte meine Erkenntnisse hier Dokumentieren. Ich versuche eine Art Tutorial aufzubauen welches aus mehreren Teilen bestehen wird und unterschiedliche Aspekte der Entwicklung mit NodeJS darstellt.

Meiner Meinung nach, macht es wenig Sinn “pures” NodeJS zur Entwicklung von Web-Anwendungen zu nutzen, man braucht also (wie sonst auch immer) ein Framework welches einem die Arbeit vereinfacht. Doch genau das will ich im ersten Schritt vermeiden, ich möchte ganz vorne anfangen. Eine simple Anwendung mit NodeJS ohne ein extra Framework.

Doch einen Anspruch möchte ich doch noch an mich stellen: Die Beispiele sollen einen funktionalen Nutzen erfüllen. Ich kann im ganzen Netz nicht mehr die üblichen Hello World Beispiele sehen. Einem wird gezeigt wie man auf der Console / im Browse “Hello World” ausgibt UND DANN? Genau das will ich nicht! Der Erste Post wird ein Spiel sein, mal sehen was die anderen bringen.

Ausschließen von Ordnern aus dem Serverseitigem Profil

Immer wieder passiert es mir, dass die Meldung “you have exceeded your profile space …” aufpoppt und ich begebe mich wieder auf die Suche nach Dateien welche ich ganz bestimmt nicht mehr brauche. Der Admin kann da einen eventuell helfen! Auf Dauer wird dieser einen aber nicht immer mehr platz auf dem Server erübrigen.

Für mich kam dann dieser RegKey zur Hilfe:

HKEY_CURRENT_USERSoftwareMicrosoftWindows NTCurrentVersionWinlogonExcludeProfileDirs

Der Key bestimmt welche Verzeichnisse Serverseitig nicht gespeichert werden sollen.

Eine interessante Erkenntnis habe ich dank des Keys erlangt. Anscheinend wird alles was unter dem Profilordner liegt, also unterhalb von C:Usersusername. Es gibt wohl einige Ordner die sind davon nochmal separat ausgeschlossen, aber sonst ist nur noch der Key ausschlaggebend. Ich hab den Wert bei mir nun folgendermaßen gesetzt:

AppDataLocal;AppDataLocalLow;$Recycle.Bin;AppData;

wie ihr sieht, ist eine Angabe nur Relativ vom Profilordner möglich. Mich würde mal brennend interessieren wie ich das ganze Profil ausschließen kann da ich Daten, welche auf unterschiedlichen Systemen benötigt werden, ehe im Netz vorhalte. Klar kann ich mich auch an den Admin wenden :)

Hello World

Hurra! Ich hab es geschafft! Mein Blog, den ich schon seit Wochen … nein seit Monaten … nein seit JAHREN plane, hat es nun endlich in die Weiten des Netzes geschafft. Primär möchte ich ihn als eine Gedächtnisstütze nutzen für Artikel, Code Snippets oder sonstigen Blödsinn. Damit das Ganze nicht auf meiner Festplatte schlecht wird, stelle ich diese Online, vielleicht hilft es ja dem einen oder anderem.