Some time ago I had the requirement to limit the number of requests per IP to a CouchDB-instance. I couldn’t find an option for CouchDB to achieve that, but as communication with CouchDB is based on HTTP-requests I thought that it should be possible to use nginx as reverse proxy with rate-limiting capabilities for CouchDB. The CouchDB-wiki lists some basic steps how to use nginx as reverse proxy, but rate limiting isn’t mentioned there.

Beside the actual rate limiting I noticed that nginx won’t work, because it doesn’t support chunked request-bodies out of the box, which CouchDB seems to rely onto. But to the rescue there is a nginx module called ngx_chunkin, which adds chunkin support to nginx. To get that module in Debian you need to install nginx-extras from the repository (available since squeeze-backports). The configuration of that module is straight forward as described on the wiki page. All you have to do is to put the following code snippet into the server-context of your nginx configuration:

        chunkin on;

        error_page 411 = @my_411_error;
        location @my_411_error {
                chunkin_resume;
        }

Having solved that issue, doing the actual rate limiting was the next step. Therefore nginx ships the module ngx_http_limit_req_module. All I had to do was to configure a zone for the limited requests in the http-context of the configuration:

limit_req_zone $binary_remote_addr zone=couchdb_write:10m rate=10r/s;

and the logic what to rate limit (writing requests in my case) into the server-context:

        location / {
                # POST, PUT, DELETE ... requests will get rate limiting
                if ($request_method !~* GET) {
                        rewrite ^(.*)$ /throttled$1 last;
                }

                proxy_pass       http://127.0.0.1:5985;
                proxy_buffering  off;
                proxy_set_header Host $host;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }

        location /throttled {
                internal;
                limit_req        zone=couchdb_write burst=10000;
                rewrite /throttled(.*) $1 break;
                proxy_pass       http://127.0.0.1:5985;
                proxy_buffering  off;
                proxy_set_header Host $host;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }

So if you put together the chunkin support and the rate limiting, you’ll get the following piece of configuration for a server which does rate limiting of writing CouchDB-requests using nginx:

limit_req_zone $binary_remote_addr zone=couchdb_write:10m rate=10r/s;

server {
        listen 5984;

        chunkin on;

        error_page 411 = @my_411_error;
        location @my_411_error {
                chunkin_resume;
        }

        location / {
                # POST, PUT, DELETE ... requests will get rate limiting
                if ($request_method !~* GET) {
                        rewrite ^(.*)$ /throttled$1 last;
                }

                proxy_pass       http://127.0.0.1:5985;
                proxy_buffering  off;
                proxy_set_header Host $host;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }

        location /throttled {
                internal;
                limit_req        zone=couchdb_write burst=10000;
                rewrite /throttled(.*) $1 break;
                proxy_pass       http://127.0.0.1:5985;
                proxy_buffering  off;
                proxy_set_header Host $host;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }
}

I really enjoyed figuring that out, because it was a lot of fun to be able to use a great tool (nginx) to extend another great tool (CouchDB), because they’re using the same protocol (HTTP).

die geladenen Elemente auf der "Microsite" von IKEA PAXAuf der deutschen IKEA-Webseite gibt es aktuell eine PAX-“Microsite”. Diese ist echt schick. So bekommt man beispielsweise durch scrollen verschiedene Schrankkombinationen und Befüllungen innerhalb desselben Bildes angezeigt. Auch werden moderne Technologien, wie zum Beispiel jQuery und Modernizr, benutzt.
Was mich dann aber doch etwas staunend zurück ließ ist die Tatsache, dass beim Aufruf der Seite über 1500 HTTP-Requests ausgelöst werden und die damit angeforderten Daten eine Gesamtgröße von über 35MB haben? Srsly? Das geht doch auch besser.

Die CDU Nord in Hamburg möchte gerne Luft-Tankstellen für Radfahrer aufstellen. Öffentliche Luftpumpen mögen ja nicht schlecht sein, allerdings wird man garantiert immer dort Luft brauchen, wo eben keine dieser Tankstellen zu finden ist.

Doch wie Sven schon treffend anmerkte hat Hamburg viel gravierende Probleme für Radfahrer als fehlende Luftpumpen am Straßenrand. Da ich vor knapp zwei Monaten von Karlsruhe nach Hamburg gezogen bin und täglich etliche Kilometer Fahrrad fahre, habe ich einen direkten Vergleich, was die Qualität von Radwegen in diesen beiden Städten anbelangt. Und ich gebe gerne offen zu: Ich weine Karlsruhe hinterher. Auch Karlsruhe war im Bezug auf Radwege nicht perfekt, aber die letzten Jahre wurden die Radwege dort konsequent ausgebaut und verbessert. Und das auf einem ziemlich hohen Niveau.

Hamburg dagegen hat den reinsten Flickenteppich im Bezug auf Radwege. Wenn es überhaupt welche gibt, beginnen und enden diese oft abrupt, sind extrem schmal und führen in Schlangenlinien über die Bürgersteige. Dazu kommt die oftmals extrem mangelhafte Beschilderung (und eine inflationäre Nutzung von Verkehrszeichen 237), durch die nicht klar ist, ob ein Teil des Bürgersteiges ein Radweg ist und ob man diesen daraufhin befahren darf.
Auch die Bordsteinabsenkungen an Kreuzungen sind oft nur im Bereich des ausgewiesenen Radweges tatsächlich bis auf Straßenniveau abgesenkt. Im Bereich der Fußgänger bleiben da meist noch ein paar Zentimeter übrig. Das ist als Radfahrer dann nervig, wenn gerade mal Fußgänger den Radwegteil zur Überquerung einer Ampelkreuzung nutzen und man auf den Fußgängerteil ausweichen muss. Auch für Kinderwägen und Rollstuhlfahrer wäre eine Absenkung von Fußübergängen an Kreuzungen bis auf Straßenniveau sicher hilfreich oder sollen diese da den Radweg nutzen?!

Ich möchte gerne mal erleben, wie die Politiker die hier in Hamburg für Straßenbaumaßnahmen und Entscheidungen zu Radwegen zuständig sind, nachts diverse Radwege in Hamburg befahren.

Und dann gibt es natürlich noch die Autofahrer, die entweder mit ihrem fetten BMW quer in einer Smart-großen Parklücke parken und somit den gesamten Radweg blockieren (heute erst erlebt) oder die einen wie blöde anhupen, weil man den nicht-benutzungspflichtigen und viel zu eng gehaltenen Radweg nicht nutzt und stattdessen auf der Straße fährt. Großstadt eben. Zum Thema Fahrradklau könnte ich auch noch Geschichten erzählen, aber das mache ich ein anderes Mal.

Ferrero hat vor Kurzem als Ergänzung zur “kinder Überraschung” ein “Mädchen-Ei” auf den Markt gebracht. Dieses “Mädchen-Ei” richtet sich, wie der Name schon sagt, explizit an Mädchen und enthält “Mädchensachen” wie “Blumen-Ringe oder bunte Armbänder mit Tiermotiven”.
Die Einführung begründet Ferrero wie folgt:

Der Grund für diese Maßnahme? Erkenntnisse der Markforschung inspirierten kinder Überraschung dazu. Die besagen, dass sich Mädchen heutzutage nicht mehr in nur eine Schublade stecken lassen. Pink und Ponyhof ist ihnen genau so wichtig, wie Fußball und Frauenpower. Eigene Erhebungen haben diesen Trend bestätigt.

Ferrero suggeriert damit, dass sich Mädchen tendenziell eher mit “Fußball und Frauenpower” beschäftigen, als mit “Pink und Ponyhof”. Die Tatsache, dass sie sich mit “Pink und Ponyhof” beschäftigen, wird als das Ergebnis einer historischen Entwicklung dargestellt. Was für eine verlogene Marketingscheiße!

Wieso wird eigentlich über Frauenquote in Führungsetagen diskutiert, aber nicht, wie man Kinder davor schützen kann, solche überholten Geschlechterrollen alltäglich indoktriniert zu bekommen?

Nach intensiven Gesprächen denke ich oft daran, dass ich bestimmte Argumente oder Perspektiven, die das Gespräch bereichert hätten, nicht angebracht habe, weil ich im passenden Moment einfach nicht daran gedacht habe. Aber auch wenn ich mich dann ärgere, so ändert das nichts daran, dass solche Gespräche einfach nur gut und wertvoll sind.

Ursprünglich mochte ich NetworkManager nicht. Grundsätzlich machte er mehr Probleme bei der Handhabung von Netzwerkverbindungen unter Linux, als er löste. Das ist allerdings schon einige Jahre her und spätestens seit der grandiosen Integration von WWAN-Hardware ist er ein richtig brauchbares Hilfsmittel geworden.

Eine Sache, die mir nun fehlte, war die Möglichkeit nach dem verbinden in ein WLAN eine Authentifizierung via Browser zu automatisieren. Nach einer kurzen Suche stellte sich heraus, dass es mit NetworkManager ein leichtes ist, auch das zu automatisieren. Dazu reicht es ein Shell-Skript in /etc/NetworkManager/dispatcher.d/ abzulegen, dass als ersten Parameter den Namen der betroffenen Verbindung und als zweiten Parameter die Statusänderung der Verbindung (up oder down) erwartet. Daraufhin war es ein leichtes, ein kleines Skript zu basteln, dass die Authentifizierung via HTTP übernimmt, nachdem eine bestimmte Verbindung aktiviert wurde:

#!/bin/sh

# network manager connection name
CONN_NAME="sample_wlan"

# check if this script is triggered for the wlan interface
if [ "$1" != "wlan0" ]; then
    exit 0;
fi

# check if the action is the enabling of the interface
if [ "$2" = "up" ]; then
    # check if the connection we want to automate, is active
    if [ "$(nmcli con status | grep -o $CONN_NAME)" = "$CONN_NAME" ]; then
        wget -q --post-data="username=john&password=123456&possible_other_post_parameters=xyz" https://fqdn-of-the-captive-portal.tld/login.html -O - > /dev/null
    fi
fi

Finde den Fehler:

#!/usr/bin/env python

import sys
if sys.version_info.major < 2 or (sys.version_info.major == 2 and sys.version_info.minor < 7):
    print "python <2.7"
else:
    print "python >=2.7"

Auflösung:
Der Ansatz ist eine nette Idee, funktioniert allerdings erst ab Python 2.7, da Python erst seit dem die benannten Parameter .major und .minor kennt. Damit der Code auch mit Python-Versionen kleiner als 2.7 funktioniert, müsste er wie folgt aussehen:

#!/usr/bin/env python

import sys

if sys.version_info[0] < 2 or (sys.version_info[0] == 2 and sys.version_info[1] < 7):
    print "python <2.7"
else:
    print "python >=2.7"

Siehe auch: http://docs.python.org/library/sys.html#sys.version_info

Das sind Kleinigkeiten, auf die man stößt, wenn man versucht ein auf einem System mit Python 2.7 geschriebenes Skript mit Python 2.6 auszuführen. Wäre ja aber langweilig, wenn alles auf Anhieb glatt laufen würde.

Innerhalb der letzten paar Wochen hatte ich das Vergnügen mehrere ejabberd-Instanzen mit vollkommen unterschiedlichen Use-Cases aufsetzen zu dürfen. Abgesehen davon, dass das Format der Konfigurationsdateien von ejabberd ein wenig gewöhnungsbedürftig ist und ejabberd in diversen Fehlerfällen keine sonderlich aussagekräftigen Fehlermeldungen produziert, waren das sehr gute Erfahrungen. Ich war jedes Mal wieder erstaunt, mit wie wenig Anpassungen an der Konfiguration ejabberd das tat, was er tun sollte.
Die Use-Cases will ich euch natürlich auch nicht vorenthalten. Das waren grob die Folgenden:

  • Instanz mit LDAP-Authentifizierung und Shared-LDAP-Roster
  • Instanz ohne S2S, dafür mit HTTP-Binding nach XEP-0124
  • Instanz mit virtuellen Hosts und Authentifizierung über ein externes Skript

Im Jahr 2007 war ich PHP und all die damit verbundenen Nachteile satt und stieß auf der Suche nach einer Alternative zur Webentwicklung auf Python und das damals noch recht junge Webframework Django. Es war Liebe auf den ersten Blick. Während ich mich in Django einarbeitete und die ersten kleinen “Apps” schrieb, hatte ich regelmäßig aha-Effekte, wenn ich sah, wie einfach sich verschiedene Funktionalitäten implementieren ließen und das wievielfache an PHP-Code man für die gleiche Funktionalität gebraucht hätte.
Doch nicht nur für Webentwicklung lässt sich Django wunderbar nutzen. Wie ich damals recht schnell feststellte, lassen sich aufgrund der Modularität von Django, einzelne Komponenten wunderbar eigenständig nutzen.
Zwei Möglichkeiten möchte ich im Folgenden kurz vorstellen.

Für ein Projekt brauchte ich die Möglichkeit statische Webseiten aus einem via Cron laufenden Skriptes zu generieren. Was bot sich also mehr an, als die Templateengine von Django zu verwenden?
Für ein simples Beispiel reichen sogar schon acht Zeilen Code:

#!/bin/env python

from django.conf import settings
from django.template.loader import render_to_string

settings.configure(TEMPLATE_DIRS=('/home/john/project/templates',))
rendered = render_to_string('template.html', { 'foo': 'cookie' })
print rendered

Das dazugehörige Template könnte wie folgt aussehen:

This is a simple template with {{ foo }}.

Das Ganze lässt sich natürlich nicht nur für HTML-Dateien, sondern für jede Art von Textdateien verwenden.
Ein anderes Anwendungsbeispiel ist die Nutzung des Object-Relational Mappers (ORM) von Django, für hübschen Datenbankzugriff aus beliebigen Python-Anwendungen:

#!/bin/env python

from django.conf import settings

settings.configure(DATABASES={
                    'default': {
                        'ENGINE': 'django.db.backends.sqlite3',
                        'NAME': 'database.sqlite'
                    }
})

from django.db import models

class Animal(models.Model):
    species = models.CharField(max_length=32)
    name = models.CharField(max_length=32)
    
    class Meta:
        app_label = 'animals'

Animal(species='dog', name='rex').save()

for i in Entry.objects.all():
    print i.name

Zu obigem Beispiel seien ein paar Punkte angemerkt:

  • Dass der Import der Models nach dem Konfigurieren der Einstellungen kommt ist Absicht. Django benötigt die Einstellungen zum Laden der Models.
  • Die Meta-Klasse mit der Option app_label muss explizit angegeben werden, sofern man den ORM ohne den Rest von Django nutzt.
  • Das obige Beispiel wird so nicht funktionieren, weil keine Datenbank mit entsprechenden Tabellen existiert. Diese müsste man (in diesem Beispiel) auf anderem Wege anlegen.

Wie man sieht, ist Django toll. Und das nicht nur für Webanwendungen, sondern für alles Mögliche, was auf Python basiert. Wollte ich nur mal gesagt haben.

Da es sinnvoll ist, regelmäßig alte Zöpfe ab zu schneiden, muss diesmal ICQ daran glauen: Ab sofort bin ich nicht mehr via ICQ, sondern “nur” noch via XMPP (auch bekannt als Jabber) erreichbar. Meine JID findet ihr bei Bedarf im Impressum.

Auch das warum ist schnell erklärt: ICQ nutzt ein propritäres Protokoll, erlaubt es laut Nutzungsbedingungen nicht, andere Clients als den offiziellen ICQ-Client zu benutzen und es ist ein zentralistisches System bei einem Anbieter, welcher sich dazu noch über die Nutzungsbedingungen zusichern lässt, das geistige Eigentum aller gesendeten Nachrichten zu erhalten.

XMPP teilt diese Schwächen nicht und seine Verbreitung hat die letzten Jahre stetig zugenommen. So lässt sich beispielsweise der Facebook-Chat via XMPP nutzen und diverse E-Mail-Anbieter (wie zum Beispiel United Internet mit allen @web.de-, @gmx.de-, …-E-Mail-Adressen) bieten analog zur E-Mail-Adresse einen dazugehörigen XMPP-Account an. Außerdem kann bei Bedarf jeder seinen eigenen XMPP-Server aufsetzen und darüber kommunizieren.