Journal Sleipnir : proxy quick & dirty pour bien coder

Posté par  (site web personnel) .
Étiquettes :
30
4
oct.
2012

Salut tous,

Bon, je n'ai pas eu le temps de compiler mes liens cette fois… mais, pour me faire pardonner, je partage avec vous un petit dév que j'ai fait en go.

En fait, ça répond à un vrai problème que je rencontre lorsque je fais du dév d'interface web (css essentiellement) : comment développer simplement et agréablement des css lorsque l'application est sur un serveur ? On clone un dépôt ? On rsync/scp/nfs les fichiers ? On code sur le serveur ?
Et, comment faire pour tester tout ça, avec des données réelles, des données de prod ?

Et voilà, c'est là que Sleipnir entre en action !
Il s'agit d'un petit proxy pour lequel on peut définir des "exceptions" : suivant des patterns (des regexp), les requêtes sont interceptées et la réponse est remplacée par le contenu d'un fichier local.

Le but est donc de pouvoir naviguer sur un site, tout en servant un fichier local (typiquement un fichier css) à la place du css distant.

L'avantage est donc que je peux modifier mon css (ou js, ou autre) sur mon poste, en local, tout en testant sur un serveur distant, voire tester en live des modifs sur un serveur de prod (bouh, pas bien) ; enfin surtout avec des données de prod. Et une fois ok, commit, push, deploy, toussa.

Bon, j'espère que c'est assez clair :)

Le programme est écrit en go, c'est mon premier avec ce langage donc il y a probablement beaucoup de choses à améliorer (et n'hésitez pas ;) )

Il est sous licence BSD.

Les sources se trouvent ici : https://github.com/CrEv/Sleipnir/

La configuration se fait dans un csv avec trois colonnes :

  • pattern
  • type mime de la réponse
  • fichier à retourner

Et histoire de voir un peu comment ça marche, j'ai fait un petit programme d'exemple (à downloader sur le github) qui remplace toutes les images jpeg et png par des petits animaux comme on en trouve beaucoup sur la toile…

Amusez-vous bien :)

  • # sshfs

    Posté par  (site web personnel) . Évalué à 0.

    Euh… Je vais peut-être dire une connerie, mais sshfs, ça marchait pas ?

    Le répertoire distant monté sur ta machine locale, et tu bosses dessus avec tes outils locaux habituels…

    La lumière pense voyager plus vite que quoi que ce soit d'autre, mais c'est faux. Peu importe à quelle vitesse voyage la lumière, l'obscurité arrive toujours la première, et elle l'attend.

    • [^] # Re: sshfs

      Posté par  (site web personnel) . Évalué à 7.

      Cette solution offre l'avantage de ne pas avoir à modifier le fichier sur le serveur distant ;)

    • [^] # Re: sshfs

      Posté par  (site web personnel) . Évalué à 10.

      oui, ça pourrait dans certains cas. tout comme nfs.
      Mais c'est pas génial, surtout si on bosse à plusieurs.
      Et ça demande de toucher le serveur (d'une manière ou d'une autre) et c'est parfois à éviter / impossible.
      En fait un tel proxy permet des cas d'utilisations beaucoup plus complexes.
      Par exemple je peux l'utiliser pour tester / développer ma css linuxfr en utilisant directement le site de prod, les données, etc, et sans m'amuser à en uploader une quelque part ou changer ma conf.

  • # Mon astuce

    Posté par  . Évalué à 5.

    Je dois pas être le seul a procéder de la sorte: je teste mes modifs directement dans chrome ou firebug. Ca permet d'avoir un rendu visuel sans avoir a recharger la page.

    Après, j'ai quand même une copie sur mon pc de l'appli en question, comme ca je valide mon commit et ensuite je le déploies avec git.

    • [^] # Re: Mon astuce (2)

      Posté par  . Évalué à 2.

      Je me demande si l'utilisation d'un fichier "proxy.pac" (avec Firefox), ne permet pas justement de rediriger des url vers une autre adresse.

    • [^] # Re: Mon astuce

      Posté par  (site web personnel) . Évalué à 5.

      Ha oui, mais c'est pas comparable.
      Il m'arrive aussi, évidemment, de tester directement dans chrome / firebug mais c'est beaucoup trop limité pour un certain nombre de cas. Déjà rien que le fait de ne pas avoir de persistance est problématique si on a plusieurs pages.

      Et surtout, ça fonctionne si on fait du css "simple". Si on prend en compte les javascript (car ça permet par exemple de loader un javascript non minifié en utilisant un serveur de préprod/prod pour rigoler un peu) ou les images, c'est pas possible.
      Et si en plus (c'est mon cas présent) on utilise sass pour générer le css, alors modifier à la main le css devient bien moins évident.

    • [^] # Re: Mon astuce

      Posté par  . Évalué à 2.

      C'est ce que je pensais en lisant le journal : Firebug ne permet pas de faire ça ?

      Article Quarante-Deux : Toute personne dépassant un kilomètre de haut doit quitter le Tribunal. -- Le Roi de Cœur

      • [^] # Re: Mon astuce

        Posté par  (site web personnel) . Évalué à 5.

        Comme indiqué juste au dessus, non :

        • images
        • js
        • persistance des modifications multi pages
        • confort (éditeur)
        • modification d'un contenu compilé / minifié / …
      • [^] # Re: Mon astuce

        Posté par  . Évalué à 5.

        Tel que je le comprends, Sleipnir est une bombe pour travailler sur de la mise en page d'un site en ligne. J'aime beaucoup !!!!

        Firebug s'attache souvent à une seule page, ca marche mais c'est carrement moins confortable.

  • # trop colonnes ?

    Posté par  (site web personnel) . Évalué à 3.

    Je me suis permis (comme souvent) d'éditer ton journal pour les typos et ortho.

    Néanmoins, il y en a une qui m'a interloqué :

    La configuration se fait dans un csv avec trop colonnes :

    j'ai s/trop/trois/ au vu du contexte, mais comment en es-tu arrivé là ?

     

    Par ailleurs, pourquoi avoir oublié de préciser une licence (libre) pour ton journal ? :D

    J'attends avec impatience de compléter ma veille technologique semaine prochaine :)

    • [^] # Re: trop colonnes ?

      Posté par  (site web personnel) . Évalué à 2.

      Je me suis permis (comme souvent) d'éditer ton journal pour les typos et ortho.

      Merci :)
      C'est vrai que je suis pas très bon en orthographe et que, honnêtement, ça ne me passionne pas vraiment… (mais je tente d'améliorer un peu)
      Heureusement que tu est là pour passer derrière :)

      mais comment en es-tu arrivé là ?

      Franchement j'en sais rien du tout. Mais vu que j'écrivais tout en regardant un film c'est probablement pour ça.

      Par ailleurs, pourquoi avoir oublié de préciser une licence (libre) pour ton journal ? :D

      Hum, je sais pas. Il est vrai que je ne coche jamais la petite case, sans vraiment de raison. Je vais étudier la question. D'ailleurs (et ta remarque suivant m'y fait penser) ça pourrait aussi être intéressant que ce soit le cas pour mes journaux de veille…
      A voir dès le prochain numéro (j'ai des liens dans les tuyaux j'espère pouvoir en sortir un journal pour la semaine prochaine)

    • [^] # Re: trop colonnes ?

      Posté par  . Évalué à 2. Dernière modification le 05 octobre 2012 à 23:06.

      La configuration se fait dans un csv avec trop colonnes :

      j'ai s/trop/trois/ au vu du contexte, mais comment en es-tu arrivé là ?

      Je pense que c'est volontaire. D'après moi c'est du langage de djeuns utilisé de manière ironique.

      Trop à la place de très, comme dans « C'est trop bien ».

      De manière ironique, car trois colonnes effectivement c'est peu.

      Marrant j'avais lu trop de colonnes moi :/

      • [^] # Re: trop colonnes ?

        Posté par  (site web personnel) . Évalué à 2.

        Je pense que c'est volontaire. D'après moi c'est du langage de djeuns utilisé de manière ironique.

        Bien tenté mais non, c'était une vrai phote ;)

  • # go

    Posté par  (site web personnel) . Évalué à 8.

    Je me disais bien que j'avais oublié un point, go.

    C'est donc mon premier programme en go et je dois avouer que je suis très agréablement surpris.
    Surpris par la simplicité de mise en oeuvre.
    Surpris par la syntaxe qui, au premier abord, ne me plaisait pas trop (:= par exemple) mais qui s'avère claire et concise.
    Surpris par l'expressivité du langage.
    Très agréablement surpris par le comma ok. Je trouve que c'est vraiment agréable comme façon de traiter les erreurs, vraiment.
    Surpris par le nombre de libs standard et non standard accessibles, pour un langage "jeune" c'est vraiment très bon.

    En gros, j'ai presque l'impression d'écrire un script comme je le ferai avec ruby par exemple, sauf que c'est compilé. C'est beaucoup plus simple et expressif que java. C'est beaucoup plus simple que c++ par exemple.

    Enfin voilà, que du bon de ce côté, je vais probablement continuer à faire du go, c'est vraiment sympa (et je comprend pourquoi certains s'y sont mis, par exemple pour le serveur d'images de linuxfr ;))

    Par contre il me reste des choses à faire/apprendre, je voudrais par exemple séparer le "proxy" de son appel, faire une lib quoi. Et là j'ai encore un peu de taff. Il faut que je comprenne un peu mieux comment fonctionnent les "objets" et les maps.

    Mais franchement, pour tous ceux qui n'ont jamais testé, faites le.
    Si vous ne savez pas quoi apprendre comme nouveau langage, essayez go !

    • [^] # Re: go

      Posté par  . Évalué à 6. Dernière modification le 05 octobre 2012 à 09:15.

      J'ai aussi joué avec il y a quelques temps et j'ai étais aussi surpris que toi. La syntaxe qui me rebutait un peu est rapide à prendre en main finalement.
      J'aime bien aussi le boulot qui est fait au niveau des cycles de développement : ça compile vite, on peut faire de la compilation à la volée.

      C'est très agréable à utiliser.

      J'allais oublié j'adore le principe de ton proxy. Ça me paraît très astucieux et flexible :)

      Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

    • [^] # Re: go

      Posté par  . Évalué à 2.

      J'ai regardé le go, c'est un langage intéressant, concis, facile à apprendre, avec des mécanismes innovants (goroutines, système d'objets). Mais il a un défaut rédhibitoire, c'est l'absence de generics. Les interfaces sont sensées le remplacer, mais c'est très limité, il est impossible de faire une fonction (a interface0, b interface0) en demandant au compilateur que a et b doivent être de même type. Du coup il y a des préprocesseur non officiels, mais c'est peu pratique et peu puissant. Un bon départ, mais ils ont loupés le coche pour moi, et les auteurs n'ont pas l'air de vouloir les intégrer. Le go restera un langage de niche, orienté réseau et parallélisme.

      • [^] # Re: go

        Posté par  (site web personnel) . Évalué à 2.

        hum…
        les "génériques" sont-ils vraiment obligatoires ?

        Un bon départ, mais ils ont loupés le coche
        Le go restera un langage de niche, orienté réseau et parallélisme.

        sur ce point je ne m'avancerais pas trop. Rien que de voir l'écart entre le go d'aujourd'hui et le go initial qui était plutôt un langage très système, vraiment dans l'optique du c. Alors qu'aujourd'hui ce n'est plus le cas.

        • [^] # Re: go

          Posté par  . Évalué à 2.

          Les génériques sont obligatoires pour faire des bibliothèques généralistes, je n'ai pas envie de devoir recoder un arbre rouge noir dès que j'ai besoin d'un arbre un minimum performant.

          Toutes les bibliothèques fournissant des structures de base en C++ ou en Java sont impossible à implémenter de façon sûre (pas de cast) en go. C'est un grand manque.

  • # Dans le même style en Ruby

    Posté par  . Évalué à 4.

    Super idée, j'utilisais déjà un petit proxy Ruby écrit par Torsten Becker https://gist.github.com/74107 pour de sombres histoires d'ouvertures de flux qui ne passaient pas derrière un VPN … j'y ai donc ajouté la possibilité de remplacer un fichier "serveur" par un fichier local au proxy.

    L'utilisation est simple, il suffit de préciser le répertoire où sont stockés les fichiers à remplacer à la volée (par défaut /tmp). Dès que le client fait une demande (par exemple /styles/mon_style.css), si un fichier portant le même nom existe dans le répertoire local (exemple /tmp/mon_style.css) c'est ce dernier qui sera renvoyé. Du coup c'est un peu moins modulable que ton code, mais c'est facilement adaptable pour des besoins plus pointus.

    Le code fonctionne sur une installation "minimale" (pas de gems à installer) de Ruby (1.9 testé, mais doit fonctionner en 1.8).

    Le code :

    #!/usr/bin/env ruby
    # Proxy with local file replacement based on Torsten Becker <torsten.becker@gmail.com> ruby proxy https://gist.github.com/74107
    
    require 'socket'
    require 'uri'
    
    class Proxy
      def run port, local_path
        @local_path=local_path
        puts "Start proxy on port #{port}. Local file must be stored in #{local_path}."
        begin
          # Start our server to handle connections (will raise things on errors)
          @socket = TCPServer.new port
    
          # Handle every request in another thread
          loop do
            s = @socket.accept
            Thread.new s, &method(:handle_request)
          end
    
        # CTRL-C
        rescue Interrupt
          puts 'Got Interrupt..'
        # Ensure that we release the socket on errors
        ensure
          if @socket
            @socket.close
            puts 'Socked closed..'
          end
          puts 'Quitting.'
        end
      end
    
      def handle_request to_client
        request_line = to_client.readline
    
        verb = request_line[/^\w+/]
        url = request_line[/^\w+\s+(\S+)/, 1]
        version = request_line[/HTTP\/(1\.\d)\s*$/, 1]
        uri = URI::parse url
    
        # Show what got requested
        puts((" %4s "%verb) + url)
    
        filename=uri.path.split('/')[-1];
        to_server = TCPSocket.new(uri.host, (uri.port.nil? ? 80 : uri.port))
        to_server.write("#{verb} #{uri.path}?#{uri.query} HTTP/#{version}\r\n")
    
        content_len = 0
    
        loop do
          line = to_client.readline
    
          if line =~ /^Content-Length:\s+(\d+)\s*$/
            content_len = $1.to_i
          end
    
          # Strip proxy headers
          if line =~ /^proxy/i
            next
          elsif line.strip.empty?
            to_server.write("Connection: close\r\n\r\n")
            if content_len >= 0
              to_server.write(to_client.read(content_len))
            end
    
            break
          else
            to_server.write(line)
          end
        end
    
        if FileTest.exists?(@local_path+filename) 
          to_server.close
          puts "Get file #{filename} from local directory #{@local_path}"
          to_server=File.new(@local_path+filename, "r") 
        end
    
        buff = ""
        loop do
          to_server.read(4048, buff)
          to_client.write(buff)
          break if buff.size < 4048
        end
    
        # Close the sockets
        to_client.close
        to_server.close
      end
    end
    
    def usage
      puts 'Usage: proxy.rb [port] [directory with local files]'
      exit 1
    end
    # Get parameters and start the server
    port=8008
    path="/tmp";
    ARGV.each do 
      |arg|
      if FileTest.directory?(arg) then path=arg
      elsif arg="-h" then usage
      elsif arg=~/^\d+/ then port=arg.to_i
      else usage 
      end
    end
    
    Proxy.new.run port, path
    
    
    • [^] # Re: Dans le même style en Ruby

      Posté par  (site web personnel) . Évalué à 2.

      Intéressant tout ça, c'est sympa.
      En même temps ça doit facilement être modifiable pour rajouter la conf telle que je l'ai fait dans sleipnir, il suffit de changer la récupération depuis local_path en gros

      • [^] # Re: Dans le même style en Ruby

        Posté par  . Évalué à 1.

        Oui, ça ne serait pas bien compliqué de faire évoluer le script Ruby … mais d'un autre coté la version actuelle me va très bien, c'est super simple vu qu'il n'y a pas de configuration à réaliser.

        Sinon je me suis aussi servit de mon proxy pour modifier à la volée du code HTML (afin d'enlever le "quirks mode" des pages d'une application) :

            if ! filename.nil? && filename=~/\.html$/
              to_server.each_line do
                |line|
                line.gsub!(/<!-- Put IE into quirks mode -->/, "                                ")
                to_client.print line
              end
            else
              loop do
                to_server.read(4048, buff)
                to_client.write(buff)
                break if buff.size < 4048
              end
            end
        
        

        Il faut juste faire attention à ce que la taille du fichier ne change pas (content-length).

        Depuis que j'ai ce proxy, je m'amuse bien à modifier des IHM en quelques minutes.

        Merci encore pour l'idée de départ.

Suivre le flux des commentaires

Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.