Showing posts with label development. Show all posts
Showing posts with label development. Show all posts

Thursday, July 4, 2013

WebUI component lifecycle

Once again, I'm drafting a not-too-trivial web app with Dart. I have to say that the language really does feel nice, and while it's getting better each week, it's not so unstable to have to be learned again on each upgrade.

One major attraction of Dart is the WebUI library, which is still far from complete, but changes the whole app design so much that any other option feels hacky by comparison. Unfortunately, there's still many rough edges, and while the documentation is very clear and and readable, there's still some things that aren't well laid out, specially about the intended architecture.

So, these are the things I'm struggling with right now.  I guess i'll turn some of these into questions to post on the +Dart G+ page, but first I want to spill my doubts here:

1.- Create components 'on the fly'.
All the WebUI examples are about instantiating components simply by using the tags in some HTML. Behind the scene, the compiler generates some tedious-looking Dart code that does everything; but there doesn't seem to be an easy way to just create a new component from Dart code.  After finding some hints reading between the lines of some docs, and distilling from the generated code, I came up with something like this:

  /// Adds a component to the host.
  ///     [holder] is the container Element,
  ///     [comp] is the already-created component object,
  ///     [compname] is the tagname of the component.
  void insertComponent(Element holder, WebComponent comp, String compname) {
    comp.host = new Element.html('<${compname}></${compname}>');
    _lifecycleCaller = new ComponentItem(comp)..create();
    holder.children.add(comp.host);
    _lifecycleCaller.insert();
  }

To create the component, the calling code must first create an instance of the Dart object and call this function, with the correct tagname.  Which leads to:

2.- Why can't the component class publish it's tagname?
It would be as simple as adding a final tagname = "x-tag-name"; to the autogenerated class declaration. In fact, the tagname is already inserted as a constant several times; but only for internal use, never to return to the caller.

3.- Does every component need a ComponentItem nanny?
Also, do you see the _lifecycleCaller object created to insert the component? Well, this code in fact isn't a free-standing function, it's a method of a class I use to handle "component container" (or "holder" as called on the code).  There, I also have to store this caller and use it after removing the DOM element.  I guess it's fair, given the name: it's a life cycle method caller, so it has to stay around for the whole life cycle.  But it would be nicer if the three methods it implements were simply part of the WebComponent class, so i could just call comp.create();, comp.insert(); and comp.remove(); at the appropriate moments.  Even better if both .insert() and .remove() could be called automatically at the DOM insertion/removal.

In short, this is how I'd like to create a component and insert it to a holder element:

  var comp = new MyComponent(...);
  comp.create();
  var holder = query('#componentholder');
  holder.children.add(comp.host);

or even:

  var comp = new MyComponent(...);
  query('#componentholder').children.add(comp..create()..host);

which is even more readable.

While writing this, I get more and more the feeling that I'm getting needlessly complicated.  It doesn't seem to be an easier way already implemented (in part because the generated code does similar things), but I can't fight the feeling that maybe I don't need any of this.

Saturday, April 7, 2012

SSH para dummies decididos

Recientemente, aconsejé a un amigo que use ssh para manejar un server; me sorprendió el número de problemas que tuvo, a pesar de tener larga experiencia con todo tipo de sistemas, incluso varios tipos de programación desde hace muchos años.

¿Que paso? No es que mi amigo estuviera poco preparado, ni tampoco que le haya sugerido algo muy difícil o complicado.  No, yo creo que el problema está en que nunca había tenido necesidad de usarlo, porque ha podido usar varias otras soluciones en cada caso que se le ha presentado.

Entonces, ¿por que mi insistencia en que use ssh, en lugar de esas otras opciones que ya conoce? Pues resulta que no sólo ssh es una única herramienta que hace muchas cosas, ayudando en muchos problemas diferentes; sino que en la mayoría de los casos es, por mucho, la mejor solución disponible.

Como tantas otras cosas, una vez dominado abre un sinnúmero de posibilidades que antes uno no habría imaginado que eran posibles, ni que estaba a sólo un paso de poder resolver problemas aparentemente difíciles con tanta facilidad que a veces ni vale la pena grabar la solución para usarla mas tarde. ¡Es igualmente fácil volver a resolverla desde cero!

Muy bien, bastante propaganda.  ¿Que hace el dichoso ssh?  En principio es simple: crea una conexión encriptada entre un cliente y un server, y sobre ella puede abrir un terminal o un túnel para otros programas. también incluye utilitarios para copiar files.  Para mantener la mayor seguridad, incluye un muy buen sistema de identificación y autentificación.  Y finalmente, es usado por varios otros programas o sistemas para facilitar toda clase de conexiones seguras y transferencias de datos.

Nota: aunque 'ssh' es el nombre original de un programa que ahora es comercial, existe también una versión Open Source cuyo nombre real es OpenSSH.  Personalmente, sólo he usado la versión Open Source y es de ella que hablo en esta nota; pero al igual que todo el mundo, la llamo simplemente 'ssh'.

Teoría:

Todo se basa en un esquema llamado Criptografía Asimétrica (o Criptografía de Clave Pública, o Public-key Cryptography).  En este esquema, cada individuo posee dos claves relacionadas: una clave pública y una clave privada.  Ambas son generadas como un par, y poseen varias propiedades matemáticas que las hacen muy útiles.

La clave pública se puede distribuir abiertamente, con la intención de que todo el mundo sepa a quién pertenece. La clave privada, en cambio debe permanecer en estricto secreto.  Si por algún motivo hay posibilidades de que una clave privada haya sido copiada, es importante revocar la contraparte pública y generar un nuevo par.

En principio, cuando se una una de estas claves para codificar un mensaje cualquiera, sólo se puede decodificar usando la otra clave. En esto se diferencia de la criptografía simétrica, o de una sola clave. De este modo, es fácil enviar un mensaje que sólo lo pueda leer una persona: basta con usar la clave pública del destinatario y sólo esa persona será capaz de decodificarlo.

Del mismo modo, si una persona codifica con su clave privada un mensaje, cualquiera puede decodificarlo usando la clave pública.  Esto sería útil para asegurar quién es el autor del mensaje.  Por comodidad, no suele codificarse el mensaje completo, sino sólo un checksum o hash generado con el contenido del mensaje, formando una "firma digital" que puede agregarse al mensaje para asegurar no sólo la autoría del mensaje, sino que no ha sido alterado desde que fue firmado.

Finalmente, dos personas pueden establecer un canal privado de comunicación usando un par de claves para cada uno. El "intercambio de claves Diffie-Hellman" es un algoritmo que cada uno de los dos involucrados realiza usando su propia clave privada y la clave pública del otro para calcular una tercera clave, que es la misma para ambos, sin necesidad de transmitirla en ningún momento.  Luego pueden usar esta clave común para aplicar una codificación simétrica a cualquier mensaje que deseen intercambiar.

Aplicación:

Tanto ssh como SSL usan la criptografía asimétrica, y principalmente el intercambio Diffie-Hellman para asegurar privacidad, seguridad y autenticidad en la transmisión de datos. Las claves utilizadas para el protocolo SSL se manejan en los llamados 'certificados', y existe una extensa infraestructura de entidades firmantes y procesos establecidos para distribuir dichos certificados.  Esto lo hace altamente práctico para encapsular otros protocolos de forma automática; pero esto implica una serie de requisitos que pueden ser muy confusos en su aplicación.

En cambio, ssh es una aplicación independiente, con su propio juego de claves en la que uno mismo es responsable de crear el par, distribuir la clave pública y proteger la privada, así como mantener la relación de identidad con las claves.

Uso básico:

El uso básico del ssh como cliente es simple:
    ssh [username@]hostname
Este comando intenta abrir una conexión con el server hostname, puerto TCP 22, intercambia varias claves, a veces hace algunas preguntas al usuario, intenta establecer la identidad de cada uno, quizás pregunte un pasword; y si todo va bien, abre un shell para ejecutar comandos remotamente de forma interactiva.
Si no se especifica username (separado del hostname con una '@') intenta usar el mismo nombre de usuario que en el cliente.

Identificación del server:

La primera vez que un cliente se conecta con un server determinado, el ssh presenta al usuario con una serie de números hexadecimales y la pregunta:
    The authenticity of host 'shell.example.com (x.x.x.x)' can't be established.
    ECDSA key fingerprint is f7:ae:3a:90:de:f6:54:90:df:f2:e2:82:fc:62:64:d4.
    Are you sure you want to continue connecting (yes/no)?
Intimidante, ¿no? Es la primera vez, y empieza advirtiendo que no sabe si el server es el correcto.

Si uno lo piensa con cuidado, tiene sentido.  Si es la primera vez, no tiene forma de saber si estamos conectando con quien queremos conectarnos.  Usualmente no hay motivo para dudarlo, de modo que basta con responder 'yes'.

En casos de alta seguridad, o si hay motivos para creer que la red se encuentra comprometida, el responsable del server puede buscar alguna forma de enviar de antemano el 'fingerprint' del server para que el usuario lo compare con esa serie de hexadecimales.

En cualquier caso, el cliente ssh registra la clave pública del server, y la próxima vez que se conecte con el mismo server, no debe aparecer esa advertencia.  A menos que ocurra algún cambio en el server y en la nueva conexión utilice una clave pública diferente.  En ese caso, el cliente muestra una advertencia mucho más severa, y se rehúsa a conectar con ese server hasta que se resuelva la discrepancia.

Supuestamente, esto sólo debería ocurrir si ha ocurrido alguna interferencia con la comunicación y el server que nos está respondiendo no es el mismo que hemos contactado antes en la misma dirección.  Sin embargo, también ocurre lo mismo cuando el server ha sido reinstalado y se regeneró el par de claves.  En ese caso, el usuario debe borrar el registro de la clave anterior para hacer nuevamente el proceso de la primera conexión.  Para eso basta con borrar una línea del file ~/.ssh/known_hosts.  La línea exacta está indicada en el mensaje de advertencia.

Identificación del cliente:

Antes que el server permita al cliente ejecutar cualquier proceso, es necesario que acredite su identificación. Para esto existen varios métodos, los dos mas comunes son un pasword convencional, y una firma criptográfica.

El método mas conocido es el pasword convencional. Es también el menos seguro, por lo que sólo se usa cuando el cliente o el server han agotado todas las otras opciones.  Consiste simplemente en que el usuario conozca el pasword necesario para el username indicado en el server.  Por supuesto, este pasword es transmitido sólo después de haber establecido un canal seguro (usando claves criptográficas temporales, generadas en el momento de la conexión).

El segundo método es mucho más seguro y sólo resulta confuso las primeras veces. El primer paso es asegurar que el cliente tenga al menos un par estable de claves pública y privada. Luego hay que copiar la clave pública al server, de modo que cuando el cliente intente conectarse, pueda usar la contraparte privada para asegurar su identidad.

Las claves usadas por ssh para identificación suelen tener los nombres ~/.ssh/id_rsa o ~/.ssh/id_dsa, cada una con dos files, uno para la clave privada y otro con el mismo nombre mas la terminación .pub para la clave pública.

Cuando el cliente se conecta con el server, transmite un nombre de la forma username@clienthost que indica la identidad del usuario que intenta probar.  El server, entoncer verifica en el file ~/.ssh/authorized_keys si existe una línea con la clave pública para tal identidad. Si lo encuentra, se lo indica al cliente, junto con un bloque de datos aleatorio.  El cliente entonces usa su clave privada para codificar esos datos y los retransmite.  Si el server entonces es capaz de decodificarlos usando la clave pública, la identidad ha sido demostrada y se abre la conexión sin necesidad de ingresar un pasword.

ssh-keygen:

Es el programa usado para generar pares de claves.  En muchos casos, durante la instalación del paquete ssh, ya se ha generado un par de claves básico, que puede ser usado sin problemas.

De no ser así, o si es necesario un nuevo par, se usa el ssh-keygen; que permite una ampla variedad de opciones.  Las dos principales opciones son el tipo de clave, y un posible pasword.

Existen varios tipos de claves, y el ssh puede usar indistintamente varios de ellos, por lo que muchas veces no importa realmente cuál usar.  Los mas usados son RSA y DSA.  Siendo posiblemente mas común el RSA.

El pasword es una decisión personal.  En este caso, no se aplica al cliente, ni al server, ni a la conexión entre uno y otro, sino al par mismo.  Mas exactamente, a la clave privada.  La idea es: para reducir las probabilidades de que una clave privada sea robada, no se almacena directamente en el file ~/.ssh/id_rsa, sino que se encuentra codificada mediante el pasword.  Solo conociendo el pasword es posible desencriptar la clave privada para poder usarla.

Estrictamente hablando, no es necesario ponerle una clave a la clave.  De este modo, es posible abrir conexiones ssh sin que se haga ninguna pregunta al usuario, lo que puede ser importante para ejecutar comandos en un script.

Continuará...

Hasta ahora hemos visto la base teórica y cómo se conecta el cliente con el server.  En la próxima, hablaré de los diferentes usos del ssh: un shell remoto, transferencia de files, túneles, etc.

Friday, June 5, 2009

back to Qt!

After a looong intermission (all hail Lua!), I'm again writing some code in Qt.

Of course, I'm using Qt 4.5 and QtCreator, which seems all nice and shiny. It seems the perfect excuse to start using Mercurial, since it's the system en vogue right now. Even GoogleCode has added support for it. Obviously Monotone won't make it big, so better to get into the bandwagon.

But...! QtCreator supports several VCS; but not Mercurial! Bummer. Well, it means I'll have to use Git, since I'm not using svn!

Sure, there's a QtDesigner view in QtCreator, and sure, it once again changes the philosophy of how to turn designs into programs. This time it's roughly what I like: it writes a '.ui' file and (optinally) the class(es) that load it. Clicking the 'goto slot' adds the corresponding slot-handling code to the apropriate class. So far, so good. There's no automated way to remove that code; but makes sense, at least in the name of safety.

But after manually removing the added code I got a lot of compiler errors! Most of them looked like some redefinition deep into the Qt codebase. Only after setting up version control (yeah, Git) I realised that I had deleted one curly bracket too many in the class declaration. Dang, I didn't knew my C++ was so rusty not to catch that at firsts sight. Of course, that also says a lot about compiler errors.

Wasn't that the advantage of static languages? to get errors early? But what use are unreadable errors?

Wednesday, January 21, 2009

"design patterns" or workarounds?

I've just read this, and it's right on point. How many times the "patterns" mantra believers try to fit the world in their few little boxes!

Hope to find more of those 'rebellious' posts about other things that bother me... of course those that i don't agree are wrong and misguided, by definition.

Wednesday, June 18, 2008

compiles? ship it!

OK, the lock-free hash table for Lua is working! after a little cleanup of the code, i announced it on the Lua list. Let's hope it stirs some new ideas. interested? get it here!

The code is small, so reading it all and comprehending isn't so hard... the next step should be writing some real tests to see if it really works. Bracing for embarrassment...

Thursday, May 29, 2008

Lock-free concurrent Lua?

For some time now I've been thinking about how to make Lua concurrent. That is, having several threads of execution, all in the same Lua space, sharing all data; but without the Big Language Lock that effectively sequentializes execution of most of the code.

Of course, the problem is that LuaThreads (the only Lua multithreading extension that puts all threads on the same space) uses just one lock per space, and all threads fight for this one resource.

One 'obvious' solution would be to use one lock per object. A little better might be to use readers/writers locks, to allow concurrent readings. yep, should work.

But a couple of days ago I saw this: Scalable Nonblocking Data Structures on Slashdot about a guy that got lock-free hashtables with great scalability on 700+ threads (with even more cores than threads!). The article was very simplistic, and seemingly inconsistent; but one /.er pointed to the Google Talk. Now that's inspiring!

I've previously read about lock-free algorithms and data structures, but this guy (that turned to be the Cliff Click)

So, after a couple of days of mulling about it, I'm hacking some code!

The original idea was to see if it's possible to lock-free'ize Lua tables. It's no easy task. The main stumbling block is that it seems that the CAS primitives available in gcc operate on pointer-sized values; since Lua tables store values directly in the hash table (and specially on the array part of the table), it's important to swap values atomically.

Much easier, and what I'm doing now, is to write new table code, lock-free from the start.

Of course, I'm using lots of naïve code in several parts, but hope to get the main ideas set, and clean silly things later (initialization right now looks very slow). Also, I still have no idea how to do the array part, and there are some unresolved issues on table resizing; but they all seem doable. And the code is coming surprisingly clean.

:-)

Sunday, November 11, 2007

Getting Lua in Apache

Used the few hours i had available this weekend trying to see what options are there to get Lua running as an Apache2 module. Shortly put, i knew about two routes: Kepler's mod2 and mod_wombat (which is supposed to be part of apache some day)

Asked about the source for mod_wombat and promptly got what i needed (thanks Brian!). looked around in the docs. looks really similar to mod_python (and i guess to mod_perl??), it can keep a pool of LuaStates, with some code preexecuted on each process, and call a specified function to process client requests. just what i hoped.

now the bad news... it says "You may user (and I encourage you to!) the threaded MPMs"; but PHP5 doesn't run in a threaded apache. ??? i had no idea it was so limited! so i guess the preforked MPM is the most used apache build. who runs apache without PHP?

then came Kepler's mod2 time. i didn't have high hopes on this because the "build everything from scratch for each request" architecture in Kepler was what motivated me to write what later became Xavante.

looking around in the mod2 launcher code confirmed it: there's a small Lua layer that is clearly called with a new, clean LuaState for each request. useless.

but i also found a nice preprocessor switch: some #if LUA_STATE_PER_REQUEST and #if LUA_STATE_PER_PROCESS to change where and when are the LuaStates created! could that be what i was looking for? of course, i was sure that code would be far less tested than the 'standard' LUA_STATE_PER_REQUEST; but still it would be easier to debug it with the kepler guy's help. but... no, it can pre-create the LuaState, but it doesn't execute _any_ code! still waits for the client's request to load, compile and execute the main Lua files. Ugh.

in conclusion: it will be mod_wombat (as i could have guessed from the start); but it's a pity not to use it how the author "encourages" to do.

Saturday, November 10, 2007

PicasaWeb APIs vs. apps

Still haven't had any time to dig into the PicasaWeb API, just what i played around with the Python client library a couple of weeks ago. But what i want is just a very simple upload app for Linux... isn't there anyone? The absolute bestest would be the DigiKam plugin; is it going on? couldn't find any sign of advances there. Also, does it support uploading just lowres versions (like Picasa does)? and keeping track of what's already uploaded (like Picasa doesn't do)?

Saturday, November 3, 2007

XMP specs

Read (again) the Adobe XMP specification, seems pretty easy to use; but the "Media Management Schema" could take some work to get well integrated (since it's precisely for DAM applications).
The hardest part will be to see which attributes are used by current applications, and exactly how. Of course, there will be some inconsistencies between one app and the other (and quite probably between versions of the same app). I hope those would be non-critical.