1pxsolidblackhttps://1pxsolidblack.pl/2020-05-15T18:14:00+02:00capital FJARR v32020-05-15T18:14:00+02:002020-05-15T18:14:00+02:00François Schmidtstag:1pxsolidblack.pl,2020-05-15:/jarr-v3.html<p>Avant toute chose, voici le lien vers la nouvelle version de l’application : <a href="https://app.jarr.info">app.jarr.info</a></p>
<h4>Préambule</h4>
<p><span class="caps">JARR</span> est un aggrégateur et un lecteur de flux.
<span class="caps">JARR</span> signigie <em>Just Another <span class="caps">RSS</span> Reader</em> et je l’utilise et l’entretient depuis maintenant plusieurs années.</p>
<p>Après une v2 sortie silencieusement il y …</p><p>Avant toute chose, voici le lien vers la nouvelle version de l’application : <a href="https://app.jarr.info">app.jarr.info</a></p>
<h4>Préambule</h4>
<p><span class="caps">JARR</span> est un aggrégateur et un lecteur de flux.
<span class="caps">JARR</span> signigie <em>Just Another <span class="caps">RSS</span> Reader</em> et je l’utilise et l’entretient depuis maintenant plusieurs années.</p>
<p>Après une v2 sortie silencieusement il y a quelques années, cette fois ci je fais une vrai <em>release</em> pour marquer le coup.
Avant de m’étendre sur les nouveautés apportés par cette v3 je vais revenir un peu sur la version précédente. La v2 apportait très discrètement et seulement en opt-in ce que j’ai appelé les <em>clusters</em> : des groupements d’articles.</p>
<h2>Les clusters</h2>
<p>L’idée de base étant que <em>plusieurs flux peuvent référencer une même ressource</em>, j’ai implémenté à l’époque les models et l’interface pour représenter cela.
À chaque création d’article, <span class="caps">JARR</span> list d’autres articles plus ou moins récents de l’utilisateur et vérifiera s’ils ne pointent pas vers la même ressource. Si c’est le cas le nouvel article sera rajouté au regroupement d’un article existant, héritant de fait de son statut (lu / non lu, marqué comme favoris ou non).</p>
<p>Le but premier de la manœuvre étant de réduire un méta-flux (l’ensemble de tous les flux d’un utilisateur), dont le débit peut être important, en éliminant la redondance.
C’est particulièrement utile quand on souscrit à des <a href="https://fr.wikipedia.org/wiki/Planet">planet</a> ou autres aggrégateurs de flux (comme <a href="https://news.ycombinator.com/">Hacker News</a> ou sa contrepartie française <a href="https://www.journalduhacker.net/">le journal du hacker</a>) et même des <em>subreddit</em>.</p>
<p>Pour rajouter à ce groupement basic, uniquement basé sur les liens, un collègue m’a alors proposé d’aller plus loin et de permettre de regrouper des articles <em>parlant</em> de la même chose via <a href="https://fr.wikipedia.org/wiki/TF-IDF"><span class="caps">TF</span>-<span class="caps">IDF</span></a>.
Cela permt de réduire le méta flux créé par plusieurs journaux nationnaux, par exemple, qui traiteraient des mêmes nouvelles.</p>
<h4>Les clusters : retour d’expérience</h4>
<p>Tout cela était plutôt expérimental, je n’ai à l’époque mis qu’une seule option pour activer ou non le regroupement. En effet on s’aperçoit assez rapidement que :</p>
<ul>
<li>
<p>Certains flux renvoient toujours le même lien et mettent à jours la ressource au bout du lien (par exemple <a href="https://www.vigicrues.gouv.fr/rss/?CdEntVigiCru=IF12">vigicrues</a>).
Par conséquent l’intégralité du contenu de ce genre de flux sera regroupé en un seul article.
Il est donc nécessaire de pouvoir désactiver le groupement flux par flux.</p>
</li>
<li>
<p>Classer ses flux en catégorie permet, entre autre, de <em>marquer comme lu</em> (ignorer le contenu) de plusieurs flux à la fois.
Le regroupement d’article étant indépendant des catégories, il arrive que des articles d’autres catégories soient ignorés dans le processus.
Il est donc nécessaire de pouvoir désactiver le groupement pour toute une catégorie.
Il est aussi nécessaire de pouvoir marquer comme lu uniquement les articles qui ne font pas parti d’un groupement.</p>
</li>
<li>
<p>L’inverse est aussi vrai, le regroupement se faisant sur un article déjà lu, le groupement restera invisible car déjà lu.
Par défaut, si un article a été marqué comme lu sans être lu et qu’il est groupé avec un nouvel article, son status lu est changé à <em>non lu</em>.
Comme le reste ce comportement est désactivable flux par flux.</p>
</li>
<li>
<p>Le fonctionnement de l’époque était tout en <span class="caps">HTTP</span> synchrone. Le crawler envoyait une requête et le serveur web créait le nouvel article et faisait le groupement ce qui a plusieurs désavantages :</p>
<ul>
<li>
<p>Le groupement, surtout via <span class="caps">TF</span>-<span class="caps">IDF</span>, est un processus long (potentiellement trop) pour le contexte d’une requête web.</p>
</li>
<li>
<p>Par définition, plusieurs groupements peuvent être exécutés en parallèle ce qui laisse la possibilité que des articles qui, créés en même temps et qui auraient dû être regroupés ensemble ne le soient pas.</p>
</li>
</ul>
</li>
<li>
<p>L’introduction des groupements d’article a apporté son lot de complexité. La remontée la plus fréquente a été que la suppression d’un feed était devenu très longue.
La suppression a donc été rendu asynchrone et est faite par un processus d’arrière plan.</p>
</li>
</ul>
<h1>La v3 : ce qui est nouveau</h1>
<h5>Worker en arrière plan</h5>
<p>D’un point de vu très technique et backend, la nouvelle version de <span class="caps">JARR</span> tourne maintenant via <a href="https://hub.docker.com/repository/docker/jaesivsm/jarr-server">Docker</a>.
Trois pour être précis, <a href="https://hub.docker.com/repository/docker/jaesivsm/jarr-front">un</a> qui sert le Javascript pour l’interface utilisateur, <a href="https://hub.docker.com/repository/docker/jaesivsm/jarr-server">un</a> pour servir les données à cette interface utilisateur et <a href="https://hub.docker.com/repository/docker/jaesivsm/jarr-worker">un</a> worker d’arrière plan multi fonction.</p>
<p>Ce dernier lance un worker <a href="http://www.celeryproject.org/">Celery</a> qui écoute sur une base <a href="https://www.rabbitmq.com/">RabbitMQ</a>.</p>
<p>Son but principal est de rafraichir les flux selon plusieurs options de configuration (délai minimal et maximal de rafraîchissement entre autre).
Ensuite, pour chaque utilisateur, de créer les groupements pour tous les articles qui en sont dépourvus.
Enfin, il s’occupe de la suppression des flux marqués à supprimer. Pour rendre l’opération instantanée pour les utilisateurs, les flux à supprimer sont en effet simplement cachés en attendant que le worker passe pour faire le ménage.</p>
<h5>L’interface</h5>
<p>J’ai écrit la première interface de <span class="caps">JARR</span> sur React 0.14, le temps de m’occuper d’autre chose, react en était déjà à sa version 14. Autant dire que l’ancienne interface était irrécupérable.</p>
<p>J’ai donc entrepris de tout réécrire de zéro, avec cette fois à l’idée une interface compatible avec les smartphones.
Le front n’étant pas mon cœur de métier, je tiens à remercier <a href="https://github.com/clarissecarzon">Clarisse</a> sans qui l’interface ressemblerait toujours à du bootstrap de 2015.</p>
<p>Je tiens aussi à remercier un autre ancien collègue qui m’a apporté une <a href="https://github.com/jaesivsm/JARR/issues/127">code review</a> des plus instructives. Comme d’habitude il faut se pencher sur les détails mais j’ai eu révélation sur révélation en relisant mon code et en comparant avec les points d’amélioration).</p>
<h5>D’un manière générale</h5>
<p>Pour faire une liste plus exhaustive de ce qui a été amélioré :</p>
<p>Expérience utilisateur :</p>
<div>
<img src="https://1pxsolidblack.pl/img/jarr/jarr-v3.jpg"
alt="un aperçu de l'interface utilisateur de la version3"
style="width: 75%; margin: auto; display: block;" />
</div>
<ul>
<li>Meilleur interface pour l’ajout de flux <span class="caps">RSS</span>.
Comme pour la v2, <span class="caps">JARR</span> va tenter de construire un flux <span class="caps">RSS</span> à partir de n’importe quelle url (même si le protocol est manquant : <code>reddit.com/r/france</code>, ou même si la ressource n’est pas un flux <span class="caps">RSS</span> : <code>https://reddit.com/r/france/</code>).
À la différence de la v2, le flux n’est pas créé immédiatement mais un panneau avec le flux préconstruit est affiché de sorte que l’utilisateur puisse l’éditer avant de le créer.</li>
</ul>
<p><img src="https://1pxsolidblack.pl/img/jarr/jarr-v3-feed-building.png"
alt="Capture d'écran de l'interface de construction d'un flux"
style="width: 300px; margin: auto; display: block;" />
</div></p>
<ul>
<li>
<p>Modification de la suppression de flux : la suppression est maintenant instantanée et asynchrone</p>
</li>
<li>
<p>Option de contrôle du groupement d’article au niveau flux, catégories et utilisateur.
Il est désormais possible de choisir si les articles d’un flux, d’une catégorie (ou même tous les articles) peuvent être groupé.
Il est aussi possible de désactiver le groupement par <span class="caps">TFIDF</span> et le réveil (le marquage comme non lu lorqu’il est lu) d’un article par le processus de groupement.</p>
</li>
</ul>
<div>
<img src="https://1pxsolidblack.pl/img/jarr/jarr-v3-edit-cluster-option.jpg"
alt="Capture d'écran du contrôle des options de groupements d'article sur un utilisateur"
style="width: 300px; margin: auto; display: block;" />
</div>
<ul>
<li>Intégration sur mesure (pour l’instant seulement si la ressource pointent vers une image ou une vidéo youtube).
Si un type de contenu supporté est reconnu, l’interface de <span class="caps">JARR</span> créra une intégration sur mesure.</li>
</ul>
<div>
<img src="https://1pxsolidblack.pl/img/jarr/jarr-v3-img-processed-content.jpg"
alt="Capture d'écran de la présentation de contenu prérendu sur JARR"
style="width: 300px; margin: auto; display: block;" />
</div>
<ul>
<li>Interface <em>responsive</em> (le menu des flux est repliable et la listes des articles a deux versions : pour les écrans larges et étroits).</li>
</ul>
<div>
<img src="https://1pxsolidblack.pl/img/jarr/jarr-v3-narrow-view.jpg"
alt="Capture d'écran de JARR en vue étroite"
style="width: 300px; margin: auto; display: block;" />
</div>
<ul>
<li>Intégration limité avec <a href="https://github.com/RSS-Bridge/rss-bridge"><span class="caps">RSS</span>-Bridge</a> afin de fournir des flux <span class="caps">RSS</span> pour des site qui en sont dépourvus.
Sont supporté automatiquement pour l’instant Twitter, Instagram et Soundcloud.</li>
</ul>
<p><img src="https://1pxsolidblack.pl/img/jarr/jarr-v3-rss-bridge-integration.png"
alt="Capture d'écran d'un flux instagram via RSS-Bridge"
style="width: 300px; margin: auto; display: block;" />
</div></p>
<ul>
<li>Édition dans un panneau dédié des options des flux, catégories et de l’utilisateur</li>
</ul>
<div>
<img src="https://1pxsolidblack.pl/img/jarr/jarr-v3-edit-user.jpg"
alt="Capture d'écran de l'édition des options d'un utilisateur"
style="width: 350px; margin: auto; display: block;" />
</div>
<p>Côté server :</p>
<ul>
<li>
<p>Refonte totale de l’<span class="caps">API</span> via <a href="flask-restx.readthedocs.io/">Flask-restx</a></p>
</li>
<li>
<p><span class="caps">API</span> accessible via Swagger sur <a href="https://api.jarr.info/">api.jarr.info</a></p>
</li>
<li>
<p>Suppression de beaucoup de code mort</p>
</li>
<li>
<p>Support des flux Json</p>
</li>
<li>
<p>Refonte totale du <em>crawler</em>, plus facilement intégrable avec d’autres types de resources</p>
</li>
<li>
<p>Abandon de <a href="http://munin-monitoring.org/">munin</a> pour un plug <a href="https://prometheus.io/">prometheus</a></p>
</li>
</ul>
<p>Ce dernier point me permet entre autre de voir d’une façon globale, comment l’application gère le cache et les délais entre deux rafraîchissement d’un flux :</p>
<div>
<img src="https://1pxsolidblack.pl/img/jarr/last-fetched-histogram.jpg"
alt="l'histrogram des délai entre le moment d'un rafraîchissement et la dernière fois que le flux a été rafraîchi"
style="width: 75%; margin: auto; display: block;" />
</div>
<h2>À venir</h2>
<p>Bien entendu ce n’est pas fini et le développement continu. En priorité (pour la <a href="https://github.com/jaesivsm/JARR/milestone/10">v3.1</a>) j’implémenterai des fonctionnalités présentes dans la v2 mais absente de la v3 (par soucis de temps). Entre autre :</p>
<ul>
<li>
<p>L’import et l’export d’archive <span class="caps">OPML</span> (<a href="https://github.com/jaesivsm/JARR/issues/130">github</a>)</p>
</li>
<li>
<p>La recherche d’articles depuis le menu (<a href="https://github.com/jaesivsm/JARR/issues/129">github</a>)</p>
</li>
</ul>
<p>J’ai aussi quelques idées de fonctionnalités comme rendre drag-n-dropable les catégories et pouvoir les ordonner à la main.</p>
<p>Je suis bien entendu ouvert aux suggestions.
N’hésitez pas à commenter ou à ouvrir une <em>issue</em> sur le <a href="https://github.com/jaesivsm/JARR/issues">bug tracker</a> si vous rencontrez un problème ou souhaiteriez une nouvelle fonctionnalité.</p>JARR v32020-05-15T18:14:00+02:002020-05-15T18:14:00+02:00François Schmidtstag:1pxsolidblack.pl,2020-05-15:/jarr-v3-en.html<p>First of all, the new app is available here: <a href="https://app.jarr.info">app.jarr.info</a> for you to test !</p>
<h4>Preambule</h4>
<p><span class="caps">JARR</span> is a news aggregator and reader.
<span class="caps">JARR</span> stands for <em>Just Another <span class="caps">RSS</span> Reader</em> and I’ve been personnally using and developing it for the past years.</p>
<p>After a v2 silently released several …</p><p>First of all, the new app is available here: <a href="https://app.jarr.info">app.jarr.info</a> for you to test !</p>
<h4>Preambule</h4>
<p><span class="caps">JARR</span> is a news aggregator and reader.
<span class="caps">JARR</span> stands for <em>Just Another <span class="caps">RSS</span> Reader</em> and I’ve been personnally using and developing it for the past years.</p>
<p>After a v2 silently released several years ago, this time I’m making a true release.
Before getting into the new things brought by this v3, I’ll talk a bit about what was brought by the precedent version.
The v2 introduced very discretly and only by opting-in what I called <em>clusters</em> which are grouped articles.</p>
<h2>Clusters</h2>
<p>The root idea was that <em>several feeds may reference a unique resource</em>.
On this base I implemented at the time a way to represent that in <span class="caps">JARR</span>.
At each article creation, <span class="caps">JARR</span> will list the more or less recent user’s articles and will check if they do link to the same resource.
If so, the newly created article will be added to the existing cluster, inheriting logically of its status (read or unread, liked or not).</p>
<p>The original goal was to reduce the meta-feed (the feed created by all the feeds of a user) by reducing redundancy.
It’s very useful when subscribing to <a href="https://en.wikipedia.org/wiki/Planet_(software)">planets</a>, news aggregator (like <a href="https://news.ycombinator.com/">Hacker News</a>) or even <em>subreddits</em>.</p>
<p>To add to this grouping processus only based on links, a colleague proposed to go further and to group articles based on their content so we would group article treating identical subjects. It’s done through <a href="https://en.wikipedia.org/wiki/Tf%E2%80%93idf">tf-idf</a>.
It allows for example to group articles from national news outlet which are destined by essence to treat the same subjects.</p>
<h4>Clusters: lesson learned</h4>
<p>All of this was pretty experimental, and at the time I only put in place the one option to opt in or out.
I rapidly discovered that :</p>
<ul>
<li>
<p>A feed might always have one link, and only update the resource at the end of that link.
This causes all the articles of that feed to get clustered together.
The lesson from that is that a <em>feed by feed</em> control of the clustering is needed.</p>
</li>
<li>
<p>Filing the feeds under different categories allows, among other things, to mark as read entire categories.
Clustering is independant from category and sometimes article from other categories that you wanted to read later got marked as read while marking a whole category as read.
This implies two evolutions : it’s necessary to be able to deactivate clustering for a category and it’s necessary to be able to mark only article not belonging to clusters as read.</p>
</li>
<li>
<p>The reverse is also true, when clustering on an already read article happen, the new article won’t every be shown to the user.
Knowing that we made it so that if an article is clustered with an article marked as read but not really read, <span class="caps">JARR</span> will change the read status of the whole cluster to unread.
Like the rest this behavior can be disabled feed by feed.</p>
</li>
<li>
<p>The process in charge of refreshing feed in v2 did entierly by pushing <span class="caps">HTTP</span> request.
As clustering can be a somewhat time consuming process, some problems occured :</p>
<ul>
<li>
<p>Clustering, especially through tf-idf, was creating timeout errors</p>
</li>
<li>
<p>Several clustering process were executed in parallel. This meant that potentially, article were treated at the same time and missed each other when they should have been clustered together.</p>
</li>
</ul>
</li>
<li>
<p>Introducing clusters to <span class="caps">JARR</span> brought a lot of complexity in the model.
The most recurring complaint I got from users were that feed deletion began to take an awful lot of time.
The new version bring a new workflows where, when deleted, a feed is hidden and removed by a background worker.</p>
</li>
</ul>
<h1>What’s new in <span class="caps">JARR</span> v3</h1>
<h5>Background workers</h5>
<p>On very technical and backend point of view, the new version of <span class="caps">JARR</span> runs now on Docker.
Three of them to be accurate : <a href="https://hub.docker.com/repository/docker/jaesivsm/jarr-front">one</a> to serv the Javascript <span class="caps">UI</span>, <a href="https://hub.docker.com/repository/docker/jaesivsm/jarr-server">one</a> to serv data to this <span class="caps">UI</span> and a last <a href="https://hub.docker.com/repository/docker/jaesivsm/jarr-worker">one</a> which is a multitask background worker.</p>
<p>The last one runs a <a href="http://www.celeryproject.org/">Celery</a> worker which listens on a <a href="https://www.rabbitmq.com/">RabbitMQ</a> queue.</p>
<p>Its main goal is to refresh feeds according to several configuration options.
It has also the mission to cluster all pending articles. To avoid collision only one clustering process is ran by user at the same time.
At last, the worker remove pending feed suppression. Indeed, to make the operation virtually instantaneous for the user, the deleted feed are merely hidden from the user and deleted in the background when the worker has time.</p>
<h5>The user interface</h5>
<p>I wrote the first version of the interface with React 0.14.
I did let the project aside for a while and by the time I got back to it, React was already at version 14.
Needless to say that the then-coded <span class="caps">UI</span> was not salvageable.</p>
<p>I just recently finished rewriting all from scratch.
This time, with the idea of responsiveness and mobile devices in mind.
Front-end not being among my strengths, I want to thank <a href="https://github.com/clarissecarzon">Clarisse</a> without whom the interface would still look like bootstrap from 2015.</p>
<p>I also would like to thank another old colleague of mine, who made a very insightful <a href="https://github.com/jaesivsm/JARR/issues/127">code review</a>.
You have to pay attention to the details, but reading his review was enlighting to me.</p>
<h5>Overall</h5>
<p>Here’s a more exhaustive list of what has been done:</p>
<p>User eXperience:</p>
<div>
<img src="https://1pxsolidblack.pl/img/jarr/jarr-v3.jpg"
alt="an overlook of the new interface in JARRv3"
style="width: 75%; margin: auto; display: block;" />
</div>
<ul>
<li>A better interface for building and adding feeds.
Like in the v2, <span class="caps">JARR</span> is gonna try to construct a feed from any given <span class="caps">URL</span> (without scheme : <code>reddit.com/r/france</code> or even if the <span class="caps">URL</span> doesn’t link to a <span class="caps">RSS</span> or <span class="caps">JSON</span> feed : <code>https://www.reddit.com/r/france</code>).
Not like in v2, this time the feed isn’t created as soon as the form is submited.
Instead the feed is builded by the backend and sent back in a pre-filled form to the <span class="caps">UI</span>.</li>
</ul>
<p><img src="https://1pxsolidblack.pl/img/jarr/jarr-v3-feed-building.png"
alt="Screenshot of the first feed creation form"
style="width: 300px; margin: auto; display: block;" />
</div></p>
<ul>
<li>
<p>Change in the feed deletion processus: the feedback is now instantaneous and asynchronous.</p>
</li>
<li>
<p>Clustering option at feed, category and user level :
It’s now possible to chose if the article from a feed, a category (or all the articles) can be clustered or not.
It’s also possible to disable clustering through tf-idf and article wake up (marking a read feed as unread) by clustering.</p>
</li>
</ul>
<div>
<img src="https://1pxsolidblack.pl/img/jarr/jarr-v3-edit-cluster-option.jpg"
alt="Screenshot of the article grouping options"
style="width: 300px; margin: auto; display: block;" />
</div>
<ul>
<li>Proccessed content integration (only for images of youtube link for now).
If a type of supported content is recognized, <span class="caps">JARR</span> interface will create and integration for it.</li>
</ul>
<div>
<img src="https://1pxsolidblack.pl/img/jarr/jarr-v3-img-processed-content.jpg"
alt="Screenshot of preprocessed content in the JARR interface"
style="width: 300px; margin: auto; display: block;" />
</div>
<ul>
<li>Responsive design (feed menu can be hidden and article list comes in two versions: for large screen and for narrow ones).</li>
</ul>
<div>
<img src="https://1pxsolidblack.pl/img/jarr/jarr-v3-narrow-view.jpg"
alt="Screenshot of the JARR interface in narrow mode"
style="width: 300px; margin: auto; display: block;" />
</div>
<ul>
<li>A limited integration with <a href="https://github.com/RSS-Bridge/rss-bridge"><span class="caps">RSS</span>-Bridge</a> has also been realized so that <span class="caps">JARR</span> can figure out a way to serv content for website that doesn’t provide <span class="caps">RSS</span> feed.
For now, only Twitter, Instagram and Soundcloud are supported.</li>
</ul>
<p><img src="https://1pxsolidblack.pl/img/jarr/jarr-v3-rss-bridge-integration.png"
alt="Screenshot of an instagram feed through RSS-bridge"
style="width: 300px; margin: auto; display: block;" />
</div></p>
<ul>
<li>Edition in a dedicated panel for feed, categories, and user settings.</li>
</ul>
<div>
<img src="https://1pxsolidblack.pl/img/jarr/jarr-v3-edit-user.jpg"
alt="Screenshot of edition of user settings"
style="width: 350px; margin: auto; display: block;" />
</div>
<p>Server side:</p>
<ul>
<li>From scratch rewrite of the <span class="caps">API</span> through <a href="flask-restx.readthedocs.io/">Flask-restx</a></li>
<li><span class="caps">API</span> accessible via Swagger at <a href="https://api.jarr.info/">api.jarr.info</a></li>
<li>Removing of a lot of dead code and dependencies</li>
<li>Json feeds are now supported</li>
<li>Total rewrite of the <em>crawler</em></li>
<li>Drop of <a href="http://munin-monitoring.org/">munin</a> integration for a <a href="https://prometheus.io/">prometheus</a> one</li>
</ul>
<p>This last point allows me, among other things, to see globally how <span class="caps">JARR</span> handles freshness of feeds :</p>
<div>
<img src="https://1pxsolidblack.pl/img/jarr/last-fetched-histogram.jpg"
alt="histrogram of delays between refreshing of a same feed"
style="width: 75%; margin: auto; display: block;" />
</div>
<h2>What’s to come</h2>
<p>Of course, development continues ! In priority (for the <a href="https://github.com/jaesivsm/JARR/milestone/10">v3.1</a>), I’ll work on features present in v2 and missing for v3:</p>
<ul>
<li>
<p>Import and export of <span class="caps">OPML</span> archives (<a href="https://github.com/jaesivsm/JARR/issues/130">github</a>)</p>
</li>
<li>
<p>Search through articles in the menu (<a href="https://github.com/jaesivsm/JARR/issues/129">github</a>)</p>
</li>
</ul>
<p>I still have a lot of ideas for new functionalities like orderable and drag-n-dropable categories.</p>
<p>I am also of course open to suggestions.
Don’t hesitate to comment below or open an issue on the <a href="https://github.com/jaesivsm/JARR/issues">bug tracker</a> if encounter any problem or whish for a new functionality.</p>MindYourNeighbors2017-03-07T14:57:00+02:002017-03-07T14:57:00+02:00François Schmidtstag:1pxsolidblack.pl,2017-03-07:/mindyourneighbors-en.html<p>I <a href="https://github.com/jaesivsm/MindYourNeighbors/">wrote</a>, <a href="https://pypi.python.org/pypi/MindYourNeighbors/">packaged</a> and just finished <a href="https://travis-ci.org/jaesivsm/MindYourNeighbors">testing</a> <strong>MindYourNeighbors</strong>, a piece of software which allows you to run scripts depending on your network neighborhood.</p>
<h3>Why ?</h3>
<p>Several years ago, I used <a href="https://transmissionbt.com/">transmission</a> on a machine at home; it was running constantly. Transmission has a <em>turtle</em> mode which reduce its network bandwith consumption …</p><p>I <a href="https://github.com/jaesivsm/MindYourNeighbors/">wrote</a>, <a href="https://pypi.python.org/pypi/MindYourNeighbors/">packaged</a> and just finished <a href="https://travis-ci.org/jaesivsm/MindYourNeighbors">testing</a> <strong>MindYourNeighbors</strong>, a piece of software which allows you to run scripts depending on your network neighborhood.</p>
<h3>Why ?</h3>
<p>Several years ago, I used <a href="https://transmissionbt.com/">transmission</a> on a machine at home; it was running constantly. Transmission has a <em>turtle</em> mode which reduce its network bandwith consumption when it’s activated. I triggered that mode which two <a href="https://en.wikipedia.org/wiki/Cron">cron</a> lines. I set them so they would trigger the “turtle” mode when I knew they were people at home.
For various reasons, it becomed uneasy to anticipate the period when there was nobody’s home and so, in the end, transmission was always in its <em>turtle</em> mode.</p>
<h3>And so, why ?</h3>
<p>So, instead of using cron, I wrote a small script that watches the neighbors table and decide if it can run or not a script that’ll set or unset the transmission <em>turtle</em> mode.
After that I just made it so that any script could be triggered that way following a simple configuration file.</p>
<p>After I left it ran on my home linuxbox and saw it was working fine, I <a href="https://codeclimate.com/github/jaesivsm/MindYourNeighbors">cleaned</a> it up, added some tests, build a <a href="https://pypi.python.org/pypi/MindYourNeighbors/">python egg on pipy</a> and did some nice integration on <a href="https://github.com/jaesivsm/MindYourNeighbors">github</a>.</p>
<h3>How ?</h3>
<p>The principle of the script is fairly simple. It’ll run <code>ip neigh show</code> and will analyse its output line by line. If a line has the <code>REACHABLE</code> or <code>PERMANENT</code> mark, it’s considered that we have a neighbor. It’ll then pass through some filters which will exclude it or not.</p>
<p>Filters are defined in a configuration file where all section inherit from the default section.
Here is a commented and edited version of my configuration file:</p>
<div class="highlight"><pre><span></span><code><span class="k">[DEFAULT]</span>
<span class="na">threshold</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">4</span>
<span class="c1"># by default, only my pc will be considered as a neighbor</span>
<span class="na">filter_on_machines</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">my_pc</span>
<span class="c1"># you can also do some broader filter</span>
<span class="c1"># filter_on_regex = .*192\.168\.0\..* # for example this will allow you to consider as neighbor all the IPv4 address of the class B</span>
<span class="c1"># this section allows you to register device by their mac addresses</span>
<span class="c1"># if you want to filter them by name in the</span>
<span class="c1"># filter_on_machines and filter_out_machines paramters</span>
<span class="k">[known_machines]</span>
<span class="na">my_pc</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s"><mac></span>
<span class="na">my_tel</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s"><mac></span>
<span class="k">[transmission]</span>
<span class="c1"># activate the "turtle" mode when there is neighbors</span>
<span class="na">command_neighbor</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">/my/scripts/transmission-turtle true</span>
<span class="c1"># desactivate it when there is none</span>
<span class="na">command_no_neighbor</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">/my/scripts/transmission-turtle false</span>
<span class="k">[bitcoind]</span>
<span class="c1"># you can desactivate a section without having to delete it</span>
<span class="c1"># from the configuration file</span>
<span class="na">enabled</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">false</span>
<span class="na">command_neighbor</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">killall bitcoind</span>
<span class="na">command_no_neighbor</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">/opt/bitcoind -server -daemon</span>
<span class="c1"># for both section above, filters are inherited from the default section</span>
<span class="k">[wake_computer]</span>
<span class="c1"># but here, I override that default value to only filter on my_tel</span>
<span class="na">filter_on_machines</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">my_tel</span>
<span class="c1"># again, we override the default value as we don't want to wait 4 cycles</span>
<span class="na">threshold</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">2</span>
<span class="c1"># we restrict time period during which this section is activated</span>
<span class="c1"># with a cron like syntax</span>
<span class="na">cron</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">* 18-23 * * 1-5</span>
<span class="na">command_neighbor</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">wake my machine</span>
</code></pre></div>
<p>The package also provides a script that you can run from anywere: <code>myn</code>. This script, run with the <code>--output</code> and <code>--verbose</code> verbose options allows you to easily run through a buggy configuration file and analyse what you would want to match and what doesn’t match.</p>
<div class="highlight"><pre><span></span><code><span class="gp"># </span>myn<span class="w"> </span>-o<span class="w"> </span>-v
<span class="go">MindYourNeighbors: INFO - MindYourNeighbors initialized</span>
<span class="go">MindYourNeighbors: DEBUG - 'transmission' - processing section</span>
<span class="go">MindYourNeighbors: DEBUG - EXCLUDED - <mac> - MACHINE: a_random_device</span>
<span class="go">MindYourNeighbors: DEBUG - MATCH - <mac> - MACHINE: my_pc</span>
<span class="go">MindYourNeighbors: DEBUG - NO_MATCH - <mac> - MACHINE: my_tel</span>
<span class="go">MindYourNeighbors: DEBUG - cache/transmission/neighbor 2 => 2</span>
<span class="go">MindYourNeighbors: INFO - 'transmission' - cache state: {'results': ['neighbor', 'no_neighbor', 'no_neighbor', 'neighbor'], 'last_command': '/home/jaes/bin/transmission-turtle false'}</span>
<span class="go">MindYourNeighbors: INFO - 'transmission' - cache count hasn't reached threshold yet (2/4)</span>
<span class="go">MindYourNeighbors: DEBUG - section <Section: bitcoind> not enabled</span>
<span class="go">MindYourNeighbors: DEBUG - section <Section: wake_if> disabled for now</span>
</code></pre></div>
<h3>SystemD</h3>
<p>It’s possible to make SystemD daemonize MindYourNeighbors. For that, you’ll have to put this <a href="https://raw.githubusercontent.com/jaesivsm/MindYourNeighbors/master/data/mind-your-neighbors.service">file</a> in <code>/etc/systemd/system/</code> and run <code>systemctl daemon-reload</code> and <code>service mind-your-neighbors start</code>.</p>
<p>Sadly, I did not find how to package this in the correct way into the python egg. Indeed, put that file and it’s destination in the <code>setup.py</code> won’t allow a none superuser to install the egg (since they can’t write in <code>/etc/systemd/system</code>). (Yes, it is kind of a cry for help :D)</p>
<h3>What’s to come</h3>
<p>Some enhancements should be coming soon like the <a href="https://github.com/jaesivsm/MindYourNeighbors/issues/4">neighbors probing</a>, some dry run options or juste add enough test to reach a <a href="https://coveralls.io/github/jaesivsm/MindYourNeighbors?branch=master">descent coverage</a>.</p>MindYourNeighbors2017-02-28T23:59:00+02:002017-02-28T23:59:00+02:00François Schmidtstag:1pxsolidblack.pl,2017-02-28:/mindyourneighbors.html<p>J’ai <a href="https://github.com/jaesivsm/MindYourNeighbors/">écrit</a>, <a href="https://pypi.python.org/pypi/MindYourNeighbors/">packagé</a> et finis de <a href="https://travis-ci.org/jaesivsm/MindYourNeighbors">tester</a> <strong>MindYourNeighbors</strong>, un programme qui permet de déclencher des scripts en fonction de son voisinage réseau.</p>
<h3>Pourquoi ?</h3>
<p>Il y a quelques années de ça, j’utilisais <a href="https://transmissionbt.com/">transmission</a> sur une machine chez moi, qui tournait en permanence. Transmission possède un mode “lent” qui permet …</p><p>J’ai <a href="https://github.com/jaesivsm/MindYourNeighbors/">écrit</a>, <a href="https://pypi.python.org/pypi/MindYourNeighbors/">packagé</a> et finis de <a href="https://travis-ci.org/jaesivsm/MindYourNeighbors">tester</a> <strong>MindYourNeighbors</strong>, un programme qui permet de déclencher des scripts en fonction de son voisinage réseau.</p>
<h3>Pourquoi ?</h3>
<p>Il y a quelques années de ça, j’utilisais <a href="https://transmissionbt.com/">transmission</a> sur une machine chez moi, qui tournait en permanence. Transmission possède un mode “lent” qui permet d’économiser les ressources du réseau quand il est activé, et via un script et deux <a href="https://fr.wikipedia.org/wiki/Cron">cron</a>, je m’arrangeais pour que transmission soit lent le soir quand il y avait du monde à la maison, et normal le reste du temps.
Pour différentes raisons, il est devenu de plus en plus malaisé de prévoir les périodes creuses où transmission pourrait être en mode normal et il finissait par être en mode lent la plupart du temps.</p>
<h3>Et donc, pourquoi ?</h3>
<p>Donc, plutôt que d’utiliser cron, j’ai écrit un petit programme qui regarde la table des neighbors connus du kernel et décide si il peut, ou non, sortir transmission de son mode lent.
Puis j’ai simplement ouvert le principe à l’exécution de n’importe quel exécutable selon une configuration.</p>
<p>Après l’avoir laissé tourné dans une version assez peu présentable pendant plutôt longtemps sur mon serveur, j’ai finis par <a href="https://codeclimate.com/github/jaesivsm/MindYourNeighbors">nettoyer</a> un peu tout ça, rajouter des tests, en faire une archive sur <a href="https://pypi.python.org/pypi/MindYourNeighbors/">pypi</a> et intégrer tout ça joliement sur <a href="https://github.com/jaesivsm/MindYourNeighbors">github</a>.</p>
<h3>Comment ?</h3>
<p>Le principe de fonctionnement est assez trivial, le script lance la commande <code>ip neigh show</code> et ligne par ligne analyse la sortie. Si une ligne a le status <code>REACHABLE</code> ou <code>PERMANENT</code> elle est considéré comme un voisin et elle passe ensuite au travers des filtres qui pourrait l’exclure ou l’inclure (sachant que l’exclusion prime).</p>
<p>Les filtres sont définit dans un fichier de configuration dont toutes les sections héritent de la section par défaut.
Voici une version commenté de mon fichier de configuration :</p>
<div class="highlight"><pre><span></span><code><span class="k">[DEFAULT]</span>
<span class="na">threshold</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">4</span>
<span class="c1"># par défaut, seul mon_ordi sera considéré</span>
<span class="na">filter_on_machines</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">mon_ordi</span>
<span class="c1"># on peut aussi faire des filtres plus large</span>
<span class="c1"># filter_on_regex = .*192\.168\.0\..* # filtrera sur toutes les adresses IPv4 de classes B</span>
<span class="c1"># cette section sert juste à renseigner les machines par leurs</span>
<span class="c1"># adresses mac si c'est le mode de filtrage que vous choisissez</span>
<span class="k">[known_machines]</span>
<span class="na">mon_ordi</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s"><mac></span>
<span class="na">mon_tel</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s"><mac></span>
<span class="k">[transmission]</span>
<span class="c1"># activer le mode lent quand il y a des voisins</span>
<span class="na">command_neighbor</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">/my/scripts/transmission-turtle true</span>
<span class="c1"># le désactiver quand il n'y en a pas</span>
<span class="na">command_no_neighbor</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">/my/scripts/transmission-turtle false</span>
<span class="c1"># capturer la sortie sur une erreur</span>
<span class="na">error_on_stderr</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">true</span>
<span class="k">[bitcoind]</span>
<span class="c1"># Vous pouvez désactiver une section entière sans avoir à l'effacer de la conf</span>
<span class="na">enabled</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">false</span>
<span class="na">command_neighbor</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">killall bitcoind</span>
<span class="na">command_no_neighbor</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">/opt/bitcoind -server -daemon</span>
<span class="c1"># pour les deux sections ci-dessus, les filtres sont hérités de la section par défaut</span>
<span class="k">[wake_computer]</span>
<span class="c1"># mais ici on surcharge la valeur par défaut pour ne filtrer que sur mon_tel</span>
<span class="na">filter_on_machines</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">mon_tel</span>
<span class="c1"># pareil, on veut que un réveil n'attendent pas 4 rotation alors on descends le seuil</span>
<span class="na">threshold</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">2</span>
<span class="c1"># on restreint les horraires d'exécution avec une syntaxe similaire à cron</span>
<span class="na">cron</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">* 18-23 * * 1-5</span>
<span class="na">command_neighbor</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">wake my machine</span>
</code></pre></div>
<p>Un script executable à la main <code>myn</code> exécuter avec les options <code>--output</code> et <code>--verbose</code> permet de facilement debugger sont fichier de configuration en comparant les correspondance obtenue avec celle désirée.</p>
<div class="highlight"><pre><span></span><code><span class="gp"># </span>myn<span class="w"> </span>-o<span class="w"> </span>-v
<span class="go">MindYourNeighbors: INFO - MindYourNeighbors initialized</span>
<span class="go">MindYourNeighbors: DEBUG - 'transmission' - processing section</span>
<span class="go">MindYourNeighbors: DEBUG - EXCLUDED - <mac> - MACHINE: une_machine</span>
<span class="go">MindYourNeighbors: DEBUG - MATCH - <mac> - MACHINE: mon_ordi</span>
<span class="go">MindYourNeighbors: DEBUG - NO_MATCH - <mac> - MACHINE: mon_tel</span>
<span class="go">MindYourNeighbors: DEBUG - cache/transmission/neighbor 2 => 2</span>
<span class="go">MindYourNeighbors: INFO - 'transmission' - cache state: {'results': ['neighbor', 'no_neighbor', 'no_neighbor', 'neighbor'], 'last_command': '/home/jaes/bin/transmission-turtle false'}</span>
<span class="go">MindYourNeighbors: INFO - 'transmission' - cache count hasn't reached threshold yet (2/4)</span>
<span class="go">MindYourNeighbors: DEBUG - section <Section: bitcoind> not enabled</span>
<span class="go">MindYourNeighbors: DEBUG - section <Section: wake_if> disabled for now</span>
</code></pre></div>
<h3>SystemD</h3>
<p>Il est possible de faire que systemd se charge de la <em>dæmonisation</em> de MindYourNeighbors. Pour ce faire il faut placer ce <a href="https://raw.githubusercontent.com/jaesivsm/MindYourNeighbors/master/data/mind-your-neighbors.service">fichier</a> dans <code>/etc/systemd/system/</code> et d’exécuter <code>systemctl daemon-reload</code> et <code>service mind-your-neighbors start</code>.</p>
<p>Malheureusement je n’ai pasa réussi à trouver comment packager de façon correct ce fichier avec l’archive <em>pypi</em>. En effet la mettre dans le <code>setup.py</code> dans l’option <code>data_files</code> résultera en une erreur si on tente une installation sans les droits <em>root</em>. (Oui c’est un appel à l’aide aux bonnes âmes qui me lisent !).</p>
<h3>À venir</h3>
<p>Quelques améliorations sont à venir comme le <a href="https://github.com/jaesivsm/MindYourNeighbors/issues/4">probing</a>, une option pour exécuter en <em>dry run</em> ou juste rajouter assez de test pour avoir une <a href="https://coveralls.io/github/jaesivsm/MindYourNeighbors?branch=master">couverture descente</a>.</p>
<p>Malgré tout je considère le programme comme assez stable pour être publié tel quel.</p>Récupération et expiration en HTTP1.12016-11-19T12:00:00+02:002016-11-19T12:00:00+02:00François Schmidtstag:1pxsolidblack.pl,2016-11-19:/recuperation-et-expiration-en-http11.html<p>Quand je me suis attaqué à <span class="caps">JARR</span>, ma première motivation était d’écrire un crawler qui ne soit pas non seulement rapide mais qui respecte aussi tout un panel de bonnes pratiques et autres RFCs.</p>
<p>Le crawler doit en l’occurence :</p>
<ul>
<li>dans le but de limiter le traffic réseau et …</li></ul><p>Quand je me suis attaqué à <span class="caps">JARR</span>, ma première motivation était d’écrire un crawler qui ne soit pas non seulement rapide mais qui respecte aussi tout un panel de bonnes pratiques et autres RFCs.</p>
<p>Le crawler doit en l’occurence :</p>
<ul>
<li>dans le but de limiter le traffic réseau et soulager en temps <span class="caps">CPU</span> les serveurs distants : <strong>ne récupérer une resource que si elle a expiré</strong></li>
<li>dans le but de limiter la consomation de resources de mon crawler : <strong>vérifier qu’une resource a changé avant de la traiter</strong></li>
</ul>
<p>Exposé ainsi, je pense que ça peut s’appliquer à en fait n’importe quel crawler et pas seulement un qui récupère des flux <span class="caps">RSS</span>, et du coup plus largement n’importe quel client web.
Pour expliquer comment réaliser ces deux fonctionnalités je vais m’appuyer sur différents mécanismes de <span class="caps">HTTP</span> que presque tout le monde implémente plus ou moins bien. Comme base de travail je vais bien sûr utiliser la <a href="https://tools.ietf.org/html/rfc2616"><span class="caps">RFC2616</span></a> qui traitre de <span class="caps">HTTP1</span>.1.</p>
<h2>Expiration d’une ressource</h2>
<p>Il y bien sûr la possibilité de simplement récupérer une ressource en boucle à intervalle de temps régulier. Ça marche plutôt bien et ça reste un fonctionnement par défaut très utile quand une ressource ne dispose d’aucun mécanisme d’expiration.</p>
<p>Comme beaucoup de choses en <span class="caps">HTTP</span> on va passer par des entêtes.</p>
<h4>Cache-Control</h4>
<p>L’entête <a href="https://tools.ietf.org/html/rfc2616#section-14.9"><code>Cache-Control</code></a>, si il est placé dans la réponse, peut préciser différentes choses concernant le contrôle de cache sur un proxy ou par un client. La directive <a href="https://tools.ietf.org/html/rfc2616#section-14.9.3"><code>max-age</code></a>) est celle qui nous intéresse et sert à préciser le délais après laquelle une ressource est considérée comme périmée.</p>
<p>Par exemple le header suivant <code>Cache-Control: max-age=600</code> signifie que la resource expirera dix minutes après avoir reçu la requête (<a href="https://tools.ietf.org/html/rfc2616#section-14.9.3"><code>en gros</code></a>).</p>
<h4>Expires</h4>
<p>On peut aussi utiliser l’entête <a href="https://tools.ietf.org/html/rfc2616#section-14.21"><code>Expires</code></a>. Celui-ci, beaucoup plus simplement, précise directement la date à laquelle la ressource sera considéré comme périmée. La date doit être précisée dans le format spécifié par la <a href="https://tools.ietf.org/html/rfc1123"><span class="caps">RFC1123</span></a>.</p>
<p>Exemple assez explicite : <code>Expires: Sat, 19 Nov 2016 14:32:47 GMT</code>.</p>
<p>Les deux pouvant être présent en même temps la <span class="caps">RFC</span> précise à la fin de cette <a href="https://tools.ietf.org/html/rfc2616#section-14.21">section</a> que la directive <code>max-age</code> de l’entête <code>Cache-Control</code> doit prévaloir.</p>
<h5>Remarques</h5>
<p>Si la date d’expiration de la ressource est inférieure à votre délais de rafraichissement, ce la vous aidera à garder la ressources plus à jours. Si elle est supérieure, cela vous évitera de la récupérer probablement inutilement.</p>
<p>Mais ce qui est important c’est de ne pas oublier que ces valeurs ne sont que des recommandations du serveur. Il n’est pas rare par exemple de voir des valeurs pour <code>Expires</code> plusieurs années dans le passés ou le futur ! C’est parfois dû à la cohabitation de <code>max-age</code> et de <code>Expires</code> mais la conclusion est la même : le serveur peut mentir ou se tromper. Je suggère par conséquent de borner la valeur que vous allez utiliser en y appliquant une limite basse et haute.</p>
<p>Maintenant que vous avez la date d’expiration, il ne vous reste plus qu’à ne planifier la récupération qu’après cette date.</p>
<p>Ensuite, ce n’est pas parce qu’elle a expirée qu’elle a changé.</p>
<h2>Vérifier qu’une ressource a changée</h2>
<p>Pour ce faire on peut tout simplement comparer la réponse obtenue avec le résultat en cache. Mais cette comparaison implique souvent un coût (disque, réseau, base de donnée, etc) qu’on peut éviter avec, encore une fois l’usage de deux entêtes. Si le serveur reconnaît ces entêtes, il répondra avec un <a href="https://tools.ietf.org/html/rfc2616#section-10.3.5">code 304</a> qui en plus d’être vide (et donc d’économiser de la bande passante) indique qu’elle ne nécessite aucun traitement supplémentaire.</p>
<h4>Last-Modified / If-Modified-Since</h4>
<p>Vous pouvez placer l’entête <a href="https://tools.ietf.org/html/rfc2616#section-14.25"><code>If-Modified-Since</code></a> avec comme valeur la date (toujours au format de la <a href="https://tools.ietf.org/html/rfc1123"><span class="caps">RFC1123</span></a>) de la dernière fois que la ressource a été modifié.</p>
<p>Vous pouvez aussi tirer la valeur de n’importe quelle réponse du serveur contenant l’entête <a href="https://tools.ietf.org/html/rfc2616#section-14.29"><code>Last-Modified</code></a>.</p>
<h4>ETag / If-None-Match</h4>
<p>Autrement vous pouvez aussi récupérer la valeur de l’entête <a href="https://tools.ietf.org/html/rfc2616#section-14.19"><code>ETag</code></a> de n’importe quelle réponse qui le contient et le passer dans vos prochaines requêtes avec l’entête <a href="https://tools.ietf.org/html/rfc2616#section-14.26"><code>If-None-Match</code></a>.</p>
<h5>Remarques</h5>
<p>Il se peut qu’une réponse avec un code autre que <code>304</code> qui contienne un <code>ETag</code> correspondant à celui transmit dans la requête. Je n’ai pas trouvé de <span class="caps">RFC</span> spécifiant le comportement à adopter dans ce cas. Pour ce qui est de <a href="https://github.com/jaesivsm/JARR"><span class="caps">JARR</span></a> j’ai pour ma part décidé que cette réponse n’invalidait pas la ressource et ne procède donc à aucune action après cette réponse.</p>
<p>Si possible, il faut préciser <code>If-None-Match</code> et <code>If-Modified-Since</code>. Le serveur suivra différentes <a href="https://tools.ietf.org/html/rfc2616#section-13.3.4">règles</a> pour savoir comment traiter les deux entêtes.</p>JARR: Note de Production2016-10-21T00:00:00+02:002016-10-21T00:00:00+02:00François Schmidtstag:1pxsolidblack.pl,2016-10-21:/2016-10-21-jarr-note-de-production.html<p>Si vous utilisez <a href="https://jarr.info"><span class="caps">JARR</span></a> régulièrement, et plus particulièrement depuis une semaine, vous avez peut-être remarqué quelques changements.</p>
<h2>Clusters (grappes)</h2>
<p>Est en test (puisque oui, la version de <span class="caps">JARR</span> en production est souvent celle de la branche <a href="https://github.com/jaesivsm/JARR/tree/develop">develop</a>) une première version d’une feature que je prépare depuis un moment: les …</p><p>Si vous utilisez <a href="https://jarr.info"><span class="caps">JARR</span></a> régulièrement, et plus particulièrement depuis une semaine, vous avez peut-être remarqué quelques changements.</p>
<h2>Clusters (grappes)</h2>
<p>Est en test (puisque oui, la version de <span class="caps">JARR</span> en production est souvent celle de la branche <a href="https://github.com/jaesivsm/JARR/tree/develop">develop</a>) une première version d’une feature que je prépare depuis un moment: les clusters. J’en ferais un article plus détaillé bientôt, mais le but est, pour l’instant, de grouper les articles de même sources, et bientôt les articles très similaires.</p>
<h2>Les aléas de la bêta</h2>
<p>Hier vous avez aussi pu remarquer quelques problèmes avec vos articles. Il s’avère qu’un vicieux bug qui survient lors de la suppression d’utilisateur (rarement donc) s’était caché dans le code de <span class="caps">JARR</span>.</p>
<p>Ayant détruit les associations articles/clusters j’ai du les reconstruire dans la nuit. Ce fut un peut douloureux pour la base mais tout est maintenant rentré dans l’ordre.</p>
<p>Mais c’était pas sans une petite montée en charge :</p>
<p style="text-align: center; width: 100%; display: block;">
<img src="https://1pxsolidblack.pl/img/jarr/postgres_cache_ALL-day.png" />
<img src="https://1pxsolidblack.pl/img/jarr/postgres_locks_ALL-day.png" />
</p>JARR: Production Note2016-10-21T00:00:00+02:002016-10-21T00:00:00+02:00François Schmidtstag:1pxsolidblack.pl,2016-10-21:/2016-10-21-jarr-production-note-en.html<p>If you use <a href="https://jarr.info"><span class="caps">JARR</span></a> on a daily basis, and more particularly yesterday, you may have noticed some changes.</p>
<h2>Clusters</h2>
<p>Since a week or so, I pushed on my running instance the last version of the <a href="https://github.com/jaesivsm/JARR/tree/develop">develop branch</a> of <span class="caps">JARR</span>.
The main feature that this branch brings is the clusters that …</p><p>If you use <a href="https://jarr.info"><span class="caps">JARR</span></a> on a daily basis, and more particularly yesterday, you may have noticed some changes.</p>
<h2>Clusters</h2>
<p>Since a week or so, I pushed on my running instance the last version of the <a href="https://github.com/jaesivsm/JARR/tree/develop">develop branch</a> of <span class="caps">JARR</span>.
The main feature that this branch brings is the clusters that allow to group article on their source or title. I’ll talk more deeply about that later.</p>
<h2>Some <span class="caps">DB</span> problems</h2>
<p>Yesterday you may have notied some problems with your articles. As it appears a vicious bug found its way into the <span class="caps">JARR</span> code and was triggered at user deletion (which happens rarely, and that’s a relief).</p>
<p>That bug destroyed association between clusters and articles. I wrote a script that spent the night fixing that but it wasn’t without some unnecessary stress on the database:</p>
<p style="text-align: center; width: 100%; display: block;">
<img src="https://1pxsolidblack.pl/img/jarr/postgres_cache_ALL-day.png" />
<img src="https://1pxsolidblack.pl/img/jarr/postgres_locks_ALL-day.png" />
</p>Mixed Contents2016-07-23T23:01:00+02:002016-07-23T23:01:00+02:00François Schmidtstag:1pxsolidblack.pl,2016-07-23:/mixed-contents-en.html<p>Today I want to write a small bit about something that bugged me for while before I found a 20 lines of code fix that solved it all : <em>Mixed Content</em>.</p>
<p>I recently figured out many of the articles I read on <a href="https://jarr.info"><span class="caps">JARR</span></a> (my feed reader for those <a href="https://1pxsolidblack.pl/jarr-en.html">who didn’t …</a></p><p>Today I want to write a small bit about something that bugged me for while before I found a 20 lines of code fix that solved it all : <em>Mixed Content</em>.</p>
<p>I recently figured out many of the articles I read on <a href="https://jarr.info"><span class="caps">JARR</span></a> (my feed reader for those <a href="https://1pxsolidblack.pl/jarr-en.html">who didn’t follow</a>) were badly displayed. The usual and easy solution was to directly go to the website hosting the article. But it’s shame to do so if the feed is not truncated and hold every bits of data you’ll need to read the article.</p>
<p>Anyway I got into debugging mode and I realised that my web browser console were showing some errors:</p>
<p><img src="https://1pxsolidblack.pl/img/mixed-content/content-blocked.png" style="margin: auto; display: block;" alt="some blocked content (in red)" /></p>
<p>It appears that the <em>mixed contents</em> is the type of content which isn’t secured (understand, not in <span class="caps">HTTPS</span>) in a page which is (understand in <span class="caps">HTTPS</span>). Mozilla talks a bit about how firefox handles those kind of pages <a href="https://support.mozilla.org/en-US/kb/mixed-content-blocking-firefox">here</a>.</p>
<p>But I also happened to stumble upon unsecure content which was still displayed. The browser was only showing warning when displaying it and not blocking it:</p>
<p><img src="https://1pxsolidblack.pl/img/mixed-content/content-warning.png" style="margin: auto; display: block;" alt="unsecure but unblocked, a real mystery" /></p>
<p>That’s where it becomes slightly trickier, there are two types of <em>mixed content</em>, and Mozilla (again) gives details about the differences <a href="https://developer.mozilla.org/en-US/docs/Web/Security/Mixed_content">here</a>.
In a few words, <em>active</em> content is supposed to be blocked, <em>passive</em> not. <code>img</code> are supposed to be <em>passive</em> so not blocked. So why <strong>some</strong> of my images were blocked ?</p>
<p>That’s the trick, those blocked <code>ìmg</code> with there <code>src</code> attribute also had a <code>srcset</code> attribute which make <code>img</code> <em>active</em> and so blocked. As <span class="caps">JARR</span> doesn’t really need <code>srcset</code> I just implemented a small cleaner that remove those attributes when appropriate. It should close some <a href="https://github.com/jaesivsm/JARR/issues/45"><span class="caps">UX</span> problems</a>…</p>Mixed Contents2016-07-23T23:00:00+02:002016-07-23T23:00:00+02:00François Schmidtstag:1pxsolidblack.pl,2016-07-23:/mixed-contents.html<p>Dans la série des petits détails méconnus qui vous prennent la tête un moment, avant de se résoudre en une vingtaine de lignes de code ; j’ai envie de parler aujourd’hui des <em>Mixed Content</em>.</p>
<p>Je me suis aperçu que pas mal d’articles étaient mals affichés quand je les …</p><p>Dans la série des petits détails méconnus qui vous prennent la tête un moment, avant de se résoudre en une vingtaine de lignes de code ; j’ai envie de parler aujourd’hui des <em>Mixed Content</em>.</p>
<p>Je me suis aperçu que pas mal d’articles étaient mals affichés quand je les lisais dans <a href="https://jarr.info"><span class="caps">JARR</span></a> (mon agrégateur <span class="caps">RSS</span> pour ceux qui <a href="https://linuxfr.org/users/jaes/journaux/jarr-v1">ne suivrait pas</a>). La solution facile était généralement de se rendre directement sur le site qui héberge l’article en question mais c’est un peu dommage d’en arriver là quand le flux Atom/<span class="caps">RSS</span> est complet et contient toutes les données nécessaires à la lecture de l’article.</p>
<p>Bref, petite enquête et je m’aperçois que dans la console de mon butineur préféré j’ai ça :</p>
<p><img src="https://1pxsolidblack.pl/img/mixed-content/content-blocked.png" style="margin: auto; display: block;" alt="du contenu bloqué (en rouge) car non sécure" /></p>
<p>Petite explication pour ceux qui, comme moi, ignoreraient ce qu’est un <em>mixed content</em> : on parle de <em>mixed content</em> quand une page page sécurisé (comprendre en <span class="caps">HTTPS</span>) sert du contenu non sécurisé (en <span class="caps">HTTP</span> par exemple).
Mozilla explique comment c’est géré dans firefox <a href="https://support.mozilla.org/en-US/kb/mixed-content-blocking-firefox">ici</a>.</p>
<p>Ceci étant, je suis aussi tombé sur du contenu non sécurisé malgré tout affiché par mon navigateur depuis une page sécurisée. Seul un warning était affiché en console mais rien n’était bloqué :</p>
<p><img src="https://1pxsolidblack.pl/img/mixed-content/content-warning.png" style="margin: auto; display: block;" /></p>
<p>C’est là que réside la subtilité, il y a deux type de <em>mixed content</em> et (encore une fois) Mozilla explique les différences et comment elles sont gérées <a href="https://developer.mozilla.org/fr/docs/S%C3%A9curit%C3%A9/MixedContent">ici</a>.
Pour faire court, il y a le contenu <em>passif</em> et le contenu <em>actif</em>. Seul le contenu actif est censé être bloqué. Les <code>img</code> sont catégorisés comme <em>passif</em> et sont censé ne pas être bloqués, même si servit en <span class="caps">HTTP</span> dans une page en <span class="caps">HTTPS</span>. Alors pourquoi <strong>certains</strong> <code>img</code> sont bloquées ?</p>
<p>C’est là qu’il y a une astuce ! Ces <code>img</code> étaient affublés d’un attribut <code>srcset</code> (<a href="http://www.alsacreations.com/article/lire/1621-responsive-images-srcset.html">que j’ai découvert à cette occasion</a>). Cet attribut fait passer les balises <code>img</code> de <em>passive</em> à <em>active</em> et ces dernières se retrouvent donc bloquées.</p>
<p>Pour le besoin de <span class="caps">JARR</span> donc, les images des articles à afficher sont purgés de toute mention de <code>srcset</code>. Voilà qui devrait clore quelques <a href="https://github.com/jaesivsm/JARR/issues/45">petits problème d’utilisabilité</a>.</p>L’impact de la sortie de Jarr2016-04-16T12:00:00+02:002016-04-16T12:00:00+02:00François Schmidtstag:1pxsolidblack.pl,2016-04-16:/jarr-impact-sortie.html<p>J’ai écrit <a href="https://linuxfr.org/users/jaes/journaux/jarr-v1">trois quatre mots</a> sur le fait que <a href="https://github.com/jaesivsm/JARR"><span class="caps">JARR</span></a> est passé dans une version propre et stable. C’est la première release publique dirons-nous, et même si c’est sur une niche (linuxfr n’est pas franchement un média de masse), j’ai quand même eu pas mal …</p><p>J’ai écrit <a href="https://linuxfr.org/users/jaes/journaux/jarr-v1">trois quatre mots</a> sur le fait que <a href="https://github.com/jaesivsm/JARR"><span class="caps">JARR</span></a> est passé dans une version propre et stable. C’est la première release publique dirons-nous, et même si c’est sur une niche (linuxfr n’est pas franchement un média de masse), j’ai quand même eu pas mal de retour intéressants (j’ai mis tout ça sur <a href="https://github.com/jaesivsm/JARR/milestones/1.0.0%20-%20Lisboa">github</a> histoire de retravailler sur tout ça plus tard).</p>
<h2>Inscription</h2>
<p>À l’heure où j’écris ces lignes 39 nouveaux utilisateurs se sont inscrit sur <a href="https://jarr.info/"><span class="caps">JARR</span></a>. La plupart sont des comptes de tests avec seulement un ou deux flux, mais certains on quand même ajouter dans les 20 ou 30 flux.</p>
<p><img src="https://1pxsolidblack.pl/img/jarr/jarr-feeds-release-day.png" style="margin: auto; display: block;" /></p>
<p>Comme vous pouvez le voir ci-dessus, à peu près 100 feeds ont été ajouté dans les heures qui ont suivies (avec un pic à 200 mais l’utilisateur a sûrement dû détruire son compte après son test).</p>
<p>Ce qu’on peu voir sur ce graph, c’est que les retards, c’est à dire les flux qui n’avait pas été mis à jours dans l’heure, ont fait un pic au moment de l’ajout des flux. Pic qui a eux quelques échos dans les heures suivantes. Ces échos ont finit par se tasser naturellement car, le crawler ne prends qu’un nombre limité de flux à rafraîchir et va donc, par construction, répartir la charge dans le temps.</p>
<p>À noter que même si les flux vont finir par se répartir dans le temps, la répartition ne sera jamais égale par la seule action du crawler (une évolution à venir ?). Pour remédier à ça, j’ai écrit un petit <a href="https://github.com/jaesivsm/JARR/blob/master/src/manager.py#L46">utilitaire</a> qui répartira l’intégralité des flux.</p>
<h2>Stress sur la base</h2>
<p style="text-align: center; width: 100%; display: block;">
<img src="https://1pxsolidblack.pl/img/jarr/postgres_scans_release-day.png" />
<img src="https://1pxsolidblack.pl/img/jarr/postgres_size_release-day.png" />
</p>
<p>Bon, postgres a vaguement pris en poids au passage, mais ce n’est rien par rapport à la masse qu’il avait déjà (20M sur 400, moins de 5%) et même si le nombre de requête a explosé aux deux principaux imports de flux (j’imagine lors de l’import de fichiers <span class="caps">OPML</span>), tout est très vite revenu à la normal sans autres impact.</p>
<p>On peut observer que le nombre de flux a fait un cours bon à 600. C’est lors de ce pic que corresponds la prise de 10Mo de poids par la base, Ces flux ayant disparus, la base aurait du maigrir d’autant mais ce n’est pas le cas, je soupçonne une mauvais configuration qui empêcher le <span class="caps">VACUM</span> d’être éxécuté.</p>
<h2>Stress sur la machine</h2>
<p>Et pour finir voyons comment mon serveur s’est comporté :</p>
<p><img src="https://1pxsolidblack.pl/img/jarr/load-release-day.png" style="margin: auto; display: block;" /></p>
<p>Mon server s’en fout ; il n’y a aucun impact sur la charge de la machine ni par le crawling ni par l’ajout massif d’articles.</p>
<p>Bon en même temps mon Xeon à 3.1GHz qui court à côté de s’est 4Go de <span class="caps">RAM</span> m’aurait fait bien de la peine à trimer pour si peu.</p>
<h2>Conclusion</h2>
<p>Certes, c’était un mini stress test sans grands enjeux mais je suis content de voir que <span class="caps">JARR</span> a bien tenu la charge.</p>
<p>Côté crawler, ça semble suffisament optimisé pour la tâche et le fait qu’il soit et multiprocess et multithreadé n’y est sûrement pas pour rien. Je ne pense pas avoir grand chose à améliorer de ce côté là.</p>
<p>Côté server, Postgres est largement suffisant pour la tâche (ce qui n’aurait probablement pas été le cas si j’étais resté sur un sqlite qui luttait déjà avec mon compte et ses 400 flux…). La machine est surdimenssionné aussi pour l’enjeux.</p>
<p>Côté applicatif, je ne sais pas. Je n’ai pas mesuré grand chose, mais vu l’impact sur le reste, je dirais que ça va. Le bench de ce côté là sera sûrement à prévoir pour une prochaine release ou une communication sur le projet via un média peut être plus important.</p>Introducing JARR v12016-04-13T23:00:00+02:002016-04-13T23:00:00+02:00François Schmidtstag:1pxsolidblack.pl,2016-04-13:/jarr-en.html<p>It’s time I (re)present to the world a project I’ve been working on for some time now. It’s a web app that agregate feeds (<span class="caps">RSS</span>/Atom) and it let you read most of them inside your browser.</p>
<p><a class="reference external" href="https://jarr.info/"><span class="caps">JARR</span></a> (and it stands for <a class="reference external" href="https://github.com/jaesivsm/JARR">Just Another Rss Reader …</a></p><p>It’s time I (re)present to the world a project I’ve been working on for some time now. It’s a web app that agregate feeds (<span class="caps">RSS</span>/Atom) and it let you read most of them inside your browser.</p>
<p><a class="reference external" href="https://jarr.info/"><span class="caps">JARR</span></a> (and it stands for <a class="reference external" href="https://github.com/jaesivsm/JARR">Just Another Rss Reader</a>).</p>
<p>Before going through the details, you can test it by yourself by creating an account on <a class="reference external" href="https://jarr.info/">my running instance</a> !</p>
<div class="section" id="the-stack">
<h2>The Stack</h2>
<p>The project runs on python3.4 and makes a heavy use of the <a class="reference external" href="http://flask.pocoo.org/">Flask</a> framework. It’s completed with the <a class="reference external" href="http://www.sqlalchemy.org/">SQLAlchemy</a> <a class="reference external" href="https://en.wikipedia.org/wiki/Object-relational_mapping"><span class="caps">ORM</span></a> which allows various <span class="caps">SQL</span> database plug. I run my own installation against a <a class="reference external" href="http://www.postgresql.org/">PostgreSQL</a> database and it works like a charm.
Concerning the <span class="caps">UI</span>, I coded the whole thing as a <a class="reference external" href="http://reactjs.net/">ReactJS</a> one page app.</p>
<p>Let’s have a look:</p>
<a class="reference external image-reference" href="https://1pxsolidblack.pl/img/jarr/full.png"><img alt="The JARR UI" src="https://1pxsolidblack.pl/img/jarr/full.png" style="width: 70%;" /></a>
</div>
<div class="section" id="a-little-tour">
<h2>A little tour</h2>
<dl class="docutils">
<dt>As you can see on the screenshot above, the <span class="caps">UI</span> is splited in three columns. From left to right:</dt>
<dd><ul class="first last simple">
<li>the first one let you see and select your categories and feeds. You can fold categories and display only feeds with unread articles or feeds which have encountered errors.</li>
<li>the second one is the article list, which will be updated when you select a feed or a category.</li>
<li>the third and last one is the category, feed, article or article you selected.</li>
</ul>
</dd>
</dl>
<p>As some feeds don’t provide content, the right panel may not be as filled as you can see on the screenshot and you may have to go directly to the source though the link on the feed title in the article list.
A better solution has also been implemented, if you have a <a class="reference external" href="https://www.readability.com/">readability</a> key you provided either at the installation or in your profile, you’ll have a little readability button in the top right corner or your article. Clicking it will retrieve a cleaned version of your content.
You can also choose in a feed options to make that retrieving automatic.
That’s especially handy for news agregator like <a class="reference external" href="https://news.ycombinator.com/">HackerNews</a>.</p>
</div>
<div class="section" id="what-s-new">
<h2>What’s new</h2>
<p>I worked a lot on the <span class="caps">UI</span>, and it feels pretty done by now. I’ll talk about it a lot below.</p>
<p>I’m working on redoing the install process so it’d be easy to bootstrap the project.</p>
<p>The crawler works pretty fine. The queue system is pretty robust and the whole thing works pretty well, it scales great against huge work loads without consumming to much ressource. The next step for it would be to make it a daemon and make it scale automatically.</p>
</div>
<div class="section" id="mobile">
<h2>Mobile</h2>
<p>The site is somewhat compatible with smaller device.</p>
<p>Here on tablets :</p>
<a class="reference external image-reference" href="https://1pxsolidblack.pl/img/jarr/small.png"><img alt="The JARR UI" src="https://1pxsolidblack.pl/img/jarr/small.png" style="width: 50%;" /></a>
<p>And on phones :</p>
<a class="reference external image-reference" href="https://1pxsolidblack.pl/img/jarr/xsmall.png"><img alt="The JARR UI" src="https://1pxsolidblack.pl/img/jarr/xsmall.png" style="width: 25%;" /></a>
</div>
<div class="section" id="what-s-down-the-road">
<h2>What’s down the road</h2>
<p>Some coming features are listed in the <a class="reference external" href="https://github.com/jaesivsm/JARR/milestones">Github milestones</a>. Most of them are obvious <span class="caps">UI</span> improvements and utilisability tweaks (as <a class="reference external" href="https://github.com/jaesivsm/JARR/issues/3">mark an article to be read later on</a> or <a class="reference external" href="https://github.com/jaesivsm/JARR/issues/8">having a nice integration for well-known feed that misbehave or are poorly formated</a>).</p>
<p>But the main thing I’d like to implement would be an intelligent grouping feature that would regroup articles in clusters. The goal would be to regroup article on the same subject (or pointing to the same resource) so a user wouldn’t be presented with the same news if it appears in multiple feeds.
It’s a feature a bit down the road, but definitively coming !</p>
</div>
<div class="section" id="history">
<h2>History</h2>
<p>The project was initially created by <a class="reference external" href="http://www.cedricbonhomme.org/">Cédric Bonhomme</a> who I thanks a lot for letting me fiddle with his project. But, as I introduced stuffs to the project that he was less and less easy with, I thought it was time for a full fork.</p>
</div>
<div class="section" id="conclusion">
<h2>Conclusion</h2>
<p>I’d be happy to see some of you install the <a class="reference external" href="https://github.com/jaesivsm/JARR">project</a> (I worked on the install script so it’s painless :) or try it on <a class="reference external" href="https://jarr.info/">my instance</a>.
Of course, I’m welcoming all critics and <a class="reference external" href="https://github.com/jaesivsm/JARR">contributions</a> !</p>
<style type='text/css'>
.reference.external.image-reference {
text-align: center; width: 100%; display: block;}
</style></div>
Pélican2016-01-16T21:00:00+02:002016-01-16T21:00:00+02:00François Schmidtstag:1pxsolidblack.pl,2016-01-16:/pelican.html<p>Plus pour réellement m’amuser que par réelle envie de maintenir ce blog à jour, j’ai changé de moteur de blog. Inspiré par d’autres pages personnelles d’<a class="reference external" href="http://kirellbenzi.com/blog/the-infamous-first-blog-post/">autres développeurs</a>, je suis passé à <a class="reference external" href="https://github.com/getpelican/pelican/">pelican</a>.</p>
<p>Par rapport à <a class="reference external" href="https://github.com/lepture/liquidluck">liquidluck</a> que j’utilisais avant il y a quelque changements notables …</p><p>Plus pour réellement m’amuser que par réelle envie de maintenir ce blog à jour, j’ai changé de moteur de blog. Inspiré par d’autres pages personnelles d’<a class="reference external" href="http://kirellbenzi.com/blog/the-infamous-first-blog-post/">autres développeurs</a>, je suis passé à <a class="reference external" href="https://github.com/getpelican/pelican/">pelican</a>.</p>
<p>Par rapport à <a class="reference external" href="https://github.com/lepture/liquidluck">liquidluck</a> que j’utilisais avant il y a quelque changements notables (même si ça ne casse pas trois pattes à un canard, on reste dans le générateur de blog statique qui mange du markdown / restructured pour pondre du html).</p>
<p>Parmi les points à l’avantage de liquidluck, de mémoire (parce que je l’ai installé il y a un moment maintenant) ce dernier était plus facile à installer et son côté “brut de décoffrage” le rendait plus facile à hacker.</p>
<p>Bon par contre son intégration n’était vraiment pas aussi souple. Pélican offre tout un tas d’intégration, la possibilité de déployer via <em>ssh</em>, une configuration bien plus complète. Bref, le choix est sans équivoque !</p>
Jessie, g_slice_set_config et gdm32015-02-05T12:00:00+02:002016-01-17T11:00:00+02:00François Schmidtstag:1pxsolidblack.pl,2015-02-05:/console-kit.html<p>Après avoir mis à jours ma debian au boulot de <em>wheezy</em> à <em>jessie</em> impossible de lancer gdm3. Après quelques errement j’ai découvert que mon <tt class="docutils literal">/var/log/syslog</tt> était rempli de lignes de ce genre :</p>
<div class="highlight"><pre><span></span>Feb<span class="w"> </span><span class="m">5</span><span class="w"> </span><span class="m">14</span>:32:14<span class="w"> </span>evoli<span class="w"> </span>console-kit-daemon<span class="o">[</span><span class="m">4627</span><span class="o">]</span>:<span class="w"> </span><span class="o">(</span>process:4690<span class="o">)</span>:<span class="w"> </span>GLib-CRITICAL<span class="w"> </span>**:<span class="w"> </span>g_slice_set_config:<span class="w"> </span>assertion<span class="w"> </span><span class="s1">'sys_page_size == 0 …</span></pre></div><p>Après avoir mis à jours ma debian au boulot de <em>wheezy</em> à <em>jessie</em> impossible de lancer gdm3. Après quelques errement j’ai découvert que mon <tt class="docutils literal">/var/log/syslog</tt> était rempli de lignes de ce genre :</p>
<div class="highlight"><pre><span></span>Feb<span class="w"> </span><span class="m">5</span><span class="w"> </span><span class="m">14</span>:32:14<span class="w"> </span>evoli<span class="w"> </span>console-kit-daemon<span class="o">[</span><span class="m">4627</span><span class="o">]</span>:<span class="w"> </span><span class="o">(</span>process:4690<span class="o">)</span>:<span class="w"> </span>GLib-CRITICAL<span class="w"> </span>**:<span class="w"> </span>g_slice_set_config:<span class="w"> </span>assertion<span class="w"> </span><span class="s1">'sys_page_size == 0'</span><span class="w"> </span>failed
</pre></div>
<p>Une recherche google d’une partie de cette ligne donne surtout comme conseil de supprimer son <tt class="docutils literal"><span class="pre">~/.profile</span></tt> ce qui n’aide en rien. Une autre, plus approfondie, avec comme paramètre que ma carte graphique est une <em><span class="caps">GT610</span></em> et a donc besoin d’un pilote nvidia, ne donne comme conseil que de <tt class="docutils literal"><span class="pre">apt-get</span> remove <span class="pre">--purge</span></tt> tous les paquets liés de plus ou moins loin à nvidia.</p>
<p>Je le marque ici afin de peut être aidé une âme perdu qui parcourerait les tréfons du net à la recherche d’une solution :</p>
<div class="highlight"><pre><span></span>aptitude<span class="w"> </span>purge<span class="w"> </span>--purge<span class="w"> </span>consolekit
aptitude<span class="w"> </span>install<span class="w"> </span>consolekit
</pre></div>
<p>En effet, c’est ce petit malin qui est à l’origine du fail de gdm3.</p>
<p>Si il doit y avoir une leçon à tirer de cette histoire c’est bien qu’il ne sert à rien de trop chercher sur le net quand on peut lire le nom du fautif directement dans le message de log. Oui, <tt class="docutils literal"><span class="pre">console-kit-daemon</span></tt>, c’est à toi que je parle.</p>
<div class="section" id="mise-a-jours">
<h2>Mise à jours</h2>
<p>La <em>traceback</em> survient toujours sur mon server et c’est plutôt disgracieux, aussi j’ai trouvé ce <a class="reference external" href="https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=718843">rapport de bug</a> qui stipule :</p>
<blockquote>
<p>ConsoleKit only manages console logins in graphical mode, so it’s
useless on a debian 8 based headless server[1]. The way to remove and
stop console kit:</p>
<p>$ sudo apt-get remove consolekit</p>
</blockquote>
<p>On ne me le demandera pas deux fois.</p>
</div>
Disqus !2014-09-10T15:00:00+02:002014-09-10T15:00:00+02:00François Schmidtstag:1pxsolidblack.pl,2014-09-10:/disqus.html<p>J’utilise <a class="reference external" href="https://github.com/lepture/liquidluck">liquidluck</a> pour générer le rendu de ce blog et ça marche bien pour le peu que j’utilise. Bon, la prise en main n’a pas forcément été des plus facile, mais dans le genre c’est quand même dans les moteurs de blog les plus sympa que …</p><p>J’utilise <a class="reference external" href="https://github.com/lepture/liquidluck">liquidluck</a> pour générer le rendu de ce blog et ça marche bien pour le peu que j’utilise. Bon, la prise en main n’a pas forcément été des plus facile, mais dans le genre c’est quand même dans les moteurs de blog les plus sympa que j’ai trouvé.
Pour le principe : ça marche avec python (indispensable !), ça génère du contenu statique et… et c’est tout.</p>
<p>En tout cas c’est ce que je pensais ! J’ai découvert, à base de lecture de fichier de configuration et de <tt class="docutils literal">grep</tt> au travers du code source, que le moteur de blog supportait l’intégration du moteur de commentaire <a class="reference external" href="http://disqus.com/">Disqus</a>. J’avais vaguement entendu parlé du service auparavant mais c’était le moment de tester.</p>
<p>Le principe est simple : on créé son compte, on intégre leur bout de code et ça roule tout seul. Après avoir jouer peu avec leur code et être vite arrivé à la conclusion qu’il n’y avait pas grand chose à tripoter je vais juste laisser cette histoire tourner par elle même.</p>
<p>J’ai reçu plusieurs mails concernant mon <a class="reference external" href="https://github.com/jaesivsm/ing_chart">appli web pour les graphs <span class="caps">ING</span></a> et je me suis dit que ces mails auraient pu profiter à <a class="reference external" href="https://1pxsolidblack.pl/ing-chart.html">mon post</a> en parlant, au moins sous forme de commentaire. C’était ma principale et unique motivation pour trifouiller <tt class="docutils literal">liquidluck</tt> en fin de compte…</p>
<p>Bref maintenant ça marche et personne n’écrira jamais rien là dedans ! Joie.</p>
ING Chart : Visualiser ses extraits de comptes2014-06-30T12:00:00+02:002014-06-30T12:00:00+02:00François Schmidtstag:1pxsolidblack.pl,2014-06-30:/ing-chart.html<p>Il y a quelque semaines de ça, j’en ai finalement eu marre que les différents graphiques de suivis de budget du site web de ma banque soient cassés.</p>
<p>Sachant que ça faisait déjà près d’un an (<a class="reference external" href="https://communaute.ingdirect.fr/t5/Sur-le-Web/Compte-courant-Fonctions-de-suivi-budget-et-d-export-HS-depuis/td-p/1395">cf le post sur le forum de support</a>) que rien n’était …</p><p>Il y a quelque semaines de ça, j’en ai finalement eu marre que les différents graphiques de suivis de budget du site web de ma banque soient cassés.</p>
<p>Sachant que ça faisait déjà près d’un an (<a class="reference external" href="https://communaute.ingdirect.fr/t5/Sur-le-Web/Compte-courant-Fonctions-de-suivi-budget-et-d-export-HS-depuis/td-p/1395">cf le post sur le forum de support</a>) que rien n’était fait à ce sujet j’ai décidé de coder moi même une petite appli web qui reproduirait les mêmes graphiques. Je me suis servi pour ça de trois bouts de <em>JavaScript</em> collé à la glue de chez <a class="reference external" href="http://jquery.com/">jquery</a> et de la bibliothèque <a class="reference external" href="http://chartjs.org/">Chart.js</a>.</p>
<p>L’appli fonctionne bien et est globalement plus esthétique / ergonomique que les graphs originaux. Pour fonctionner elle ne requiert que les historiques des mouvements de compte qui sont téléchargeables au format <em><span class="caps">CSV</span></em> depuis le site d’<span class="caps">ING</span>.</p>
<p>Le graph est utilisable à partir d’<a class="reference external" href="/ing_chart/">ici</a> et je me fends d’un petit aperçu du rendu :</p>
<a class="reference external image-reference" href="https://1pxsolidblack.pl/img/ing_chart.lines.png"><img alt="Lines chart example" class="align-center" src="https://1pxsolidblack.pl/img/ing_chart.lines.png" style="width: 30%;" /></a>
<p>J’ai aussi implémenté un mode camembert (<em>piechart</em> quoi) parce que j’aurais eu tort de me priver :</p>
<a class="reference external image-reference" href="https://1pxsolidblack.pl/img/ing_chart.pie.png"><img alt="Pie chart example" class="align-center" src="https://1pxsolidblack.pl/img/ing_chart.pie.png" style="width: 30%;" /></a>
<p>Bon. Ce qui est dommage, c’est qu’à l’heure où j’écris ces lignes, un correctif vient tout juste d’être plublié et les graphs fonctionnent de nouveau sur le site d’<span class="caps">ING</span> Direct…</p>
<p>Malgré tout, je trouve mon implémentation bien meilleurs ! Plus flexible, plus jolie, plus lisible, si en plus on compte sur mon rêve simplet de postérité, ça fait pas mal de raisons de laisser ma bidouille en ligne.</p>
<p>Bien entendu, tout ceci est open source et le code est disponible sur <a class="reference external" href="https://github.com/jaesivsm/ing_chart/">github</a>.</p>
<style type='text/css'>
.reference.external.image-reference {
text-align: center; width: 100%; display: block;}
</style>Servir et gérer des fichiers avec WebDav2013-12-09T21:20:00+02:002013-12-09T21:20:00+02:00François Schmidtstag:1pxsolidblack.pl,2013-12-09:/apache-webdav-file-server.html<p>J’ai récement eu besoin de pouvoir stocker des fichiers en ligne. Je voulais un minimum d’authentification, quelque chose qui soit standard, pas de client lourd etc, etc. Bref, après m’être renseigné à minima j’ai décidé de tenter ma chance avec <a href="https://httpd.apache.org/docs/2.4/fr/mod/mod_dav.html">WebDav</a> en l’occurence puisque je …</p><p>J’ai récement eu besoin de pouvoir stocker des fichiers en ligne. Je voulais un minimum d’authentification, quelque chose qui soit standard, pas de client lourd etc, etc. Bref, après m’être renseigné à minima j’ai décidé de tenter ma chance avec <a href="https://httpd.apache.org/docs/2.4/fr/mod/mod_dav.html">WebDav</a> en l’occurence puisque je fais tourner ça avec apache).</p>
<p>Les caractéristiques voulues :</p>
<ul>
<li>accès en <span class="caps">HTTP</span> et HTTPs</li>
<li>un dossier <code>/public/</code> accessible en lecture par tous (<span class="caps">HTTP</span> et HTTPs)</li>
<li>redirection de <span class="caps">HTTP</span> vers HTTPs sinon</li>
<li>authentification via <em>BasicAuth</em></li>
<li>tous les utilisateurs peuvent écrire dans <code>/public/</code></li>
<li>chaque utilisateurs ne peut écrire sur dans l’<span class="caps">URI</span> <code>/<utilisateur>/</code></li>
</ul>
<p>Maintenant la conf et les explications :</p>
<ul>
<li>La conf du <em>VirtualHost</em> pour l’accès en <span class="caps">HTTP</span> :</li>
</ul>
<div class="highlight"><pre><span></span><code><span class="nt"><VirtualHost</span><span class="w"> </span><span class="s">*:80</span><span class="nt">></span>
<span class="w"> </span><span class="nb">ServerName</span><span class="w"> </span>my.server.tld
<span class="w"> </span><span class="nb">DocumentRoot</span><span class="w"> </span>/$document_root/
<span class="w"> </span><span class="nt"><Directory</span><span class="w"> </span><span class="s">/$document_root/public/</span><span class="nt">></span>
<span class="w"> </span><span class="nb">Options</span><span class="w"> </span>-Indexes
<span class="w"> </span><span class="nt"></Directory></span>
<span class="w"> </span><span class="nt"><Directory</span><span class="w"> </span><span class="s">/$document_root/public/*/</span><span class="nt">></span>
<span class="w"> </span><span class="nb">Options</span><span class="w"> </span>+Indexes
<span class="w"> </span><span class="nt"></Directory></span>
<span class="w"> </span><span class="nb">RewriteEngine</span><span class="w"> </span><span class="k">On</span>
<span class="w"> </span><span class="c"># Cette RewriteCond vérifie que l'URI de la requête concerne le notre</span>
<span class="w"> </span><span class="c"># dossier /public/ (ou les images qui servent à l'affichage des indexes</span>
<span class="w"> </span><span class="c"># chez apache). La RewriteCond ne s'applique que pour la RewriteRule</span>
<span class="w"> </span><span class="c"># suivante.</span>
<span class="w"> </span><span class="nb">RewriteCond</span><span class="w"> </span>%{REQUEST_URI}<span class="w"> </span>^/(public/(?|.*)|icons/(?|.*)|favicon\.ico|robots\.txt|$)$
<span class="w"> </span><span class="nb">RewriteRule</span><span class="w"> </span>^.*<span class="w"> </span>-<span class="w"> </span>[L]
<span class="w"> </span><span class="c"># Cette dernière RewriteRule s'assure que tous ce qui n'a pas matché</span>
<span class="w"> </span><span class="c"># plus haut est redirigé vers le même domaine en HTTPs</span>
<span class="w"> </span><span class="nb">RewriteRule</span><span class="w"> </span>^/?(.*)<span class="w"> </span>https://%{HTTP_HOST}/$1<span class="w"> </span>[QSA,L,R=301]
<span class="w"> </span><span class="nb">ErrorLog</span><span class="w"> </span>${APACHE_LOG_DIR}/error.log
<span class="w"> </span><span class="nb">CustomLog</span><span class="w"> </span>${APACHE_LOG_DIR}/access.log<span class="w"> </span>vhost_combined
<span class="w"> </span><span class="nb">LogLevel</span><span class="w"> </span><span class="k">warn</span>
<span class="nt"></VirtualHost></span>
</code></pre></div>
<ul>
<li>La conf du <em>VirtualHost</em> pour l’accès en HTTPs :</li>
</ul>
<div class="highlight"><pre><span></span><code><span class="nt"><VirtualHost</span><span class="w"> </span><span class="s">*:443</span><span class="nt">></span>
<span class="w"> </span><span class="nb">ServerName</span><span class="w"> </span>my.server.tld
<span class="w"> </span><span class="nb">SSLEngine</span><span class="w"> </span><span class="k">on</span>
<span class="w"> </span><span class="nb">DocumentRoot</span><span class="w"> </span>/$document_root/
<span class="w"> </span><span class="nb">DavLockDB</span><span class="w"> </span><span class="sx">/run/lock/apache_dav</span>
<span class="w"> </span><span class="nb">DAVMinTimeout</span><span class="w"> </span><span class="m">600</span>
<span class="w"> </span><span class="c"># On empêche les robots des snifer le roots où il y a</span>
<span class="w"> </span><span class="c"># la liste des users (leurs dossiers perso en fait)</span>
<span class="w"> </span><span class="nt"><Directory</span><span class="w"> </span><span class="s">/$document_root/</span><span class="nt">></span>
<span class="w"> </span><span class="nb">Options</span><span class="w"> </span>-Indexes
<span class="w"> </span><span class="nb">Dav</span><span class="w"> </span><span class="k">On</span>
<span class="w"> </span><span class="nt"></Directory></span>
<span class="w"> </span><span class="c"># On définit qu'une authentification est nécessaire pour</span>
<span class="w"> </span><span class="c"># accéder à n'importe quel dossier.</span>
<span class="w"> </span><span class="nt"><Directory</span><span class="w"> </span><span class="s">/$document_root/*/</span><span class="nt">></span>
<span class="w"> </span><span class="nb">Options</span><span class="w"> </span>+Indexes<span class="w"> </span>+FollowSymLinks<span class="w"> </span>+MultiViews
<span class="w"> </span><span class="nb">AllowOverride</span><span class="w"> </span><span class="k">None</span>
<span class="w"> </span><span class="nb">Order</span><span class="w"> </span>allow,deny
<span class="w"> </span><span class="nb">Allow</span><span class="w"> </span>from<span class="w"> </span><span class="k">all</span>
<span class="w"> </span><span class="nb">Dav</span><span class="w"> </span><span class="k">On</span>
<span class="w"> </span><span class="nb">AuthType</span><span class="w"> </span>Basic
<span class="w"> </span><span class="nb">AuthName</span><span class="w"> </span><span class="s2">"My Server WebDav"</span>
<span class="w"> </span><span class="nb">AuthUserFile</span><span class="w"> </span><span class="sx">/path/to/htpasswd</span>
<span class="w"> </span><span class="nb">Require</span><span class="w"> </span>valid-user
<span class="w"> </span><span class="nt"></Directory></span>
<span class="w"> </span><span class="c"># On est pas trop méchant et on laisse</span>
<span class="w"> </span><span class="c"># quand même les robots accéder à leurs conf</span>
<span class="w"> </span><span class="nt"><Files</span><span class="w"> </span><span class="s">/$document_root/robots.txt</span><span class="nt">></span>
<span class="w"> </span><span class="nb">Order</span><span class="w"> </span>Allow,Deny
<span class="w"> </span><span class="nb">Allow</span><span class="w"> </span>from<span class="w"> </span><span class="k">all</span>
<span class="w"> </span><span class="nb">Satisfy</span><span class="w"> </span><span class="k">any</span>
<span class="w"> </span><span class="nb">Require</span><span class="w"> </span><span class="k">all</span><span class="w"> </span>granted
<span class="w"> </span><span class="nt"></Files></span>
<span class="w"> </span><span class="c"># On surcharge la précédente définition pour le dossier /public/.</span>
<span class="w"> </span><span class="nt"><Directory</span><span class="w"> </span><span class="s">/$document_root/public/</span><span class="nt">></span>
<span class="w"> </span><span class="nb">Options</span><span class="w"> </span>-Indexes<span class="w"> </span>+FollowSymLinks<span class="w"> </span>+MultiViews
<span class="w"> </span><span class="nb">AllowOverride</span><span class="w"> </span><span class="k">None</span>
<span class="w"> </span><span class="nb">Order</span><span class="w"> </span>Allow,Deny
<span class="w"> </span><span class="nb">Allow</span><span class="w"> </span>from<span class="w"> </span><span class="k">all</span>
<span class="w"> </span><span class="nb">Dav</span><span class="w"> </span><span class="k">On</span>
<span class="w"> </span><span class="nt"><LimitExcept</span><span class="w"> </span><span class="s">GET OPTIONS</span><span class="nt">></span>
<span class="w"> </span><span class="nb">AuthType</span><span class="w"> </span>Basic
<span class="w"> </span><span class="nb">AuthName</span><span class="w"> </span><span class="s2">"My Server WebDav"</span>
<span class="w"> </span><span class="nb">AuthUserFile</span><span class="w"> </span><span class="sx">/path/to/htpasswd</span>
<span class="w"> </span><span class="nb">Require</span><span class="w"> </span>valid-user
<span class="w"> </span><span class="nt"></LimitExcept></span>
<span class="w"> </span><span class="nt"></Directory></span>
<span class="w"> </span><span class="c"># On autorise les indexes sur les dossiers contenu dans /public/</span>
<span class="w"> </span><span class="c"># même si ils ne sont pas autorisés dans ce dernier</span>
<span class="w"> </span><span class="nt"><Directory</span><span class="w"> </span><span class="s">/$document_root/public/*/</span><span class="nt">></span>
<span class="w"> </span><span class="nb">Options</span><span class="w"> </span>+Indexes
<span class="w"> </span><span class="nt"></Directory></span>
<span class="w"> </span><span class="nb">RewriteEngine</span><span class="w"> </span><span class="k">On</span>
<span class="w"> </span><span class="c"># On autorise l'accès au robot.txt sans redirection aucune</span>
<span class="w"> </span><span class="nb">RewriteCond</span><span class="w"> </span>%{REQUEST_URI}<span class="w"> </span>^/robots.txt$
<span class="w"> </span><span class="nb">RewriteRule</span><span class="w"> </span>^/?(.*)<span class="w"> </span>http://%{HTTP_HOST}/robots.txt<span class="w"> </span>[QSA,L,R=301]
<span class="w"> </span><span class="c"># Comme précédemment, on ne touche à rien si</span>
<span class="w"> </span><span class="c"># l'URI de la requête concerne le dossier /public/.</span>
<span class="w"> </span><span class="nb">RewriteCond</span><span class="w"> </span>%{REQUEST_URI}<span class="w"> </span>^/(public/(?|.*)|icons/(?|.*)|favicon\.ico)$
<span class="w"> </span><span class="nb">RewriteRule</span><span class="w"> </span>^.*<span class="w"> </span>-<span class="w"> </span>[L]
<span class="w"> </span><span class="c"># La règle qui empêche un utilisateur d'accéder</span>
<span class="w"> </span><span class="c"># à un dossier qui n'est pas à eux.</span>
<span class="w"> </span><span class="nb">RewriteCond</span><span class="w"> </span>%{LA-U:REMOTE_USER}<span class="w"> </span>^(.+)
<span class="w"> </span><span class="nb">RewriteCond</span><span class="w"> </span>%1:/$1<span class="w"> </span>!^([^:]+):/\<span class="m">1</span>$
<span class="w"> </span><span class="nb">RewriteRule</span><span class="w"> </span>^/([^/]*)<span class="w"> </span>-<span class="w"> </span>[F,L]
<span class="w"> </span><span class="nb">ErrorLog</span><span class="w"> </span>${APACHE_LOG_DIR}/error.log
<span class="w"> </span><span class="nb">CustomLog</span><span class="w"> </span>${APACHE_LOG_DIR}/access.log<span class="w"> </span>vhost_combined
<span class="w"> </span><span class="nb">LogLevel</span><span class="w"> </span><span class="k">warn</span>
<span class="nt"></VirtualHost></span>
</code></pre></div>
<p>Les dernières <em>RewriteCond</em> sont utilisées pour s’assurer que chaque utilisateur n’accèdera qu’à son dossier utilisent <code>LA-U:</code>. Ce préfix est décrit dans la documentation de <a href="http://httpd.apache.org/docs/current/mod/mod_rewrite.html#rewritecond">mod_rewrite</a>.</p>
<p>L’idée derrière cette subtilité est que les règles de réécriture sont exécutées avant que l’authentification n’ait lieu. La variable <code>REMOTE_USER</code>, elle, définie lors de l’authentification, n’est pas disponible pour la comparaison avec l’<strong><span class="caps">URI</span></strong> de la requête. <code>LA-U:</code> permet de prefetch cette valeur. La dernière condition de réécriture véréfie que cette valeur correspond bien au début de l’<span class="caps">URI</span> de la requête.</p>
<p>La syntaxe de la condition de réécriture est assez compliquée (et honteusement copiée de <a href="https://maze.io/2010/02/09/restricting-directory-in-apache-per-logged-in-user/">cet article</a>). Elle doit sa structure au fait que apache ne permet pas à une valeur d’être à droite de la comparaison du <em>RewriteCond</em>.</p>