jueves, 29 de septiembre de 2016

Recorrer una estructura de directorios descomprimiendo ficheros con clave


Tengo una copia de seguridad que es una réplica de una estructura de carpetas y subcarpetas, con varios ficheros repartidos por todo el árbol de directorios.La copia replica la estructura de carpetas en una unidad externa. Cada fichero se comprime individualmente y se cifra con una contraseña.

Un fichero y su copia.  ¿Cuál es el original?
By Alvesgaspar (Own work (own photo)) [<a href="http://www.gnu.org/copyleft/fdl.html">GFDL</a> or <a href="http://creativecommons.org/licenses/by-sa/4.0-3.0-2.5-2.0-1.0">CC BY-SA 4.0-3.0-2.5-2.0-1.0</a>], <a href="https://commons.wikimedia.org/wiki/File%3APigs_July_2008-1.jpg">via Wikimedia Commons</a>

Si se trata de recuperar un fichero específico, no hay mucho problema, copio el comprimido a la carpeta de destino y con cualquier utilidad de descompresión lo extraigo manualmente, tecleando la contraseña en el momento necesario.

Pero si quiero restaurar TODA UNA CARPETA Y EL SUBÁRBOL que cuelga de ella, ir extrayendo los ficheros uno a uno no es una opción válida (son muchos), y deseo automatizar esta operación. Para ello, necesitaré una utilidad de línea de comandos que me permita extraer un archivo individual (usaré el 7za, la utilidad de línea de comandos del 7-zip) y un script que me permita ir recorriendo el árbol de directorios uno a uno y ejecutar esta utilidad en cada uno de ellos. También tendré que ir especificando la ruta destino individualmente para cada fichero, que deberé ir construyendo dinámicamente.

En esta ocasión, estoy trabajando en Windows. Tras un rato leyendo las especificaciones del comando FOR (en una ventana de comando basta con escribir "help for"), he aquí la solución al problema planteado:

set z="c:\bin\util\7za.exe"
set d="c:\ruta\destino"
set clave=",,"

for /D /R %%i in (*.* .) do %z% x -r -o%d%%%~pi%%~ni %%i\*.zip -p%clave% -y
::~ for /D /R %%i in (*.*) do @echo de %%~pi a %d%%%~pi%%~ni

No repetiré aquí las especificaciones del comando FOR, para eso está su ayuda, que considero suficiente (aunque un poco falta de claridad, lo reconozco), pero sí daré unas breves notas:

- El /D hace referencia a que trabajaremos con directorios, y /R es para que se haga un recorrido recursivo

- El comando a ejecutar con cada directorio debe ir en la misma línea que el FOR, hasta donde yo sé. Desconozco si se puede dividir la instrucción en varias líneas, lo cual ayudaría bastante a la legibilidad, pero yo no lo he conseguido.

- Las variables de entorno (establecidas en las primeras líneas con SET) se escriben como %VAR% y las variables del FOR llevan un doble símbolo de porcentaje %%VAR por estar dentro de un fichero de script. Si estuviéramos tecleándolas directamente en una ventana de comando se escribiría un único %, así: %VAR.

- Los modificadores introducidos delante del nombre de la variable (en el comando a ejecutar) sirven para extraer parte del nombre del directorio, así ~p significa PATH (sólo la ruta, sin el nombre) ~n significa NOMBRE (nombre del directorio a procesar)

- La línea comentada (la que comienza por ::~ for)  se puede utilizar para testear el resultado antes de ejecutarlo de verdad, imprimiendo las rutas a procesar, pero sin ejecutar la operación. Para probarlo, bastaría con descomentar esta línea y comentar la anterior a ella.

En la cláusula in es importante añadir, además de *.*, el punto (.) que hace referencia al directorio actual. Si no se incluye, se procesan todos los directorios que cuelgan del actual, pero no el directorio contenedor. Tampoco pasa nada, siempre podía haber copiado la carpeta a restaurar como única carpeta hija de c:\tmp, pongamos por caso, y ejecutar el script en c:\tmp directamente. Pero incluyendo el punto la solución es más genérica, pues vale tanto si en el directorio actual hay ficheros o si sólo hay directorios.

Como reflexión, pensemos que cuando hacemos una copia de seguridad, no basta con tener los ficheros guardados, sino que debemos tener previsto (¡y probado!) el sistema que utilizaremos para restaurar la información en caso necesario. De nada nos sirve tener una copia de seguridad si, en caso de necesidad, a la hora de restaurarla nos vamos a tener que esperar una eternidad a que la información vuelva a estar disponible, por tener que llevar a cabo un proceso manual o un proceso que, aunque esté automatizado, no responda en un tiempo razonable.

Había una cita conocida que no recuerdo literalmente, pero venía a decir una cosa importantísima, algo así como:

 - "No planifiques una estrategia de copias de seguridad, planifica una estrategia de recuperación"

(si alguien sabe el autor y la frase exacta, le estaré agradecido si me lo pone en un comentario).

Ahora es buen momento para probar tu procedimiento de contingencia: ¿cuánto tardarías si tuvieras que restaurar ahora mismo alguna información importante?

jueves, 28 de julio de 2016

La crème de la crème (consultas TOP-N en Oracle)

En esta entrada hablaba de cómo seleccionar las N primeras filas de una tabla. En todos los casos (Informix, SQL Server, SQLite, Oracle), hacía una consulta básica y nos quedábamos con los 10 primeros resultados. Las filas en una tabla relacional no están ordenadas, por definición, lo cual significa que la consulta nos da las N primeras filas aleatorias, es decir, sin ordenar.

Pero la cosa cambia un poco si las queremos ordenadas, es decir los mejores resultados, las mejores notas, los mejores salarios, la crème de la crème... (o los peores).

Queremos los resultados crème de la crème

Esto significa sacar las N filas primeras (o mínimas) o las N filas últimas (o máximas), lo que se denomina a veces consultas TOP-N (TOP-N queries, por si queréis buscarlo en inglés).

En el caso de SQLite3 podíamos poner la cláusula LIMIT después del ORDER BY, como se explicaba en aquella entrada. Pero en ORACLE no existe la cláusula LIMIT, así que habrá que hacerlo de alguna otra manera.

En principio, utilizando la columna "mágica" ROWNUM, si hacemos lo siguiente:

SELECT * FROM TABLA WHERE ROWNUM <= 100

estamos obteniendo 100 filas aleatorias. Uno podría pensar que entonces, para obtener las TOP-100 filas bastaría con añadir una condición ORDER BY, tal que así:

SELECT * FROM TABLA WHERE ROWNUM <= 100 ORDER BY CAMPO

Pero eso no funcionaría. Lo que nos estaría dando son 100 filas aleatorias, y una vez seleccionadas (y no antes), nos las mostraría ordenadas por el CAMPO indicado. Pero eso no significa que nos esté dando las 100 filas PRIMERAS (según en el orden indicado) de la tabla. El problema es que el valor de ROWNUM se está asignando justo al seleccionar la fila, y la operación ORDER BY se aplica después.

Así pues, la forma correcta de hacerlo en Oracle es intercalar una SELECT dentro de otra:

SELECT * FROM (la-consulta-original) WHERE ROWNUM <= 100

Es decir, para el caso que nos ocupa:

SELECT * FROM (SELECT * FROM TABLA ORDER BY CAMPO) WHERE ROWNUM <= 100


Ale, problema resuelto. A seguir tecleando.

--

Nota: no confundir ROWNUM con ROWID. Ambas son columnas que asigna ORACLE automáticamente, pero no significan lo mismo.

Si queréis una explicación muy buena y bastante detallada acerca de la diferencia entre ROWNUM y ROWID y cómo utilizar ROWNUM para consultas TOP-N y para resultados paginados, echadle un vistazo a este artículo

http://www.epidataconsulting.com/tikiwiki/tiki-read_article.php?articleId=63

martes, 19 de julio de 2016

Codificación de caracteres (charset) en una BD Oracle

Necesitaba saber cuál es el CHARSET que se está utilizando en una BD Oracle. Con la sentencia

SELECT * FROM NLS_DATABASE_PARAMETERS;

podremos saber no sólo la codificación, sino otros parámetros interesantes como el idioma, los separadores decimales y de miles, el formato de fecha y hora, la versión de la BD, el símbolo de moneda y algunas cosas más...


jueves, 23 de junio de 2016

Sesión de galletitas y galletitas de sesiones

Ayer me surgió un problemilla relacionado con sesiones y cookies, y algún pequeño misterio (aún) sin resolver. A ver si consigo explicar un poco el tema de las cookies y las sesiones para mi yo del futuro, y a ver también si alguien me puede dar una pista acerca del misterio.

1. Sesiones

Una sesión es una forma de mantener el estado utilizando un protocolo como HTTP que, por construcción, no está pensado para mantener el estado. Normalmente, una sesión dura desde que el usuario abre el navegador hasta que lo cierra. El servidor web puede pasar información entre diferentes páginas durante la misma sesión. Cuando el navegador solicita la página (esa que va a trabajar con sesiones), el servidor web le asigna un identificador de sesión (SESSID), se lo envía al cliente y le pide que lo guarde "en algún sitio" (normalmente, en una cookie). Después, cada vez que el usuario haga una petición de página, el servidor web recibirá ese SESSID en una cookie (eso es lo habitual, aunque también puede enviarse en la propia URL de acceso). Conservando cierta información ligada al identificador de la sesión (en el servidor web), puede darse un servicio personalizado al usuario, como mantener un carrito de la compra, por ejemplo. Para mantener esta información, el desarrollador web puede almacenar determinadas variables (nombre:valor) en la sesión.

El servidor web guardará la información de la sesión en algún sitio, puede ser en ficheros de texto, binarios, en una base de datos... Normalmente, una configuración por defecto de Apache + PHP implica que se guarden en ficheros de texto plano.

Vale. Hasta aquí bien.

2. Cookies

Para guardar en el navegador del cliente el ID de sesión se utilizan cookies habitualmente. El servidor web le pide al navegador del usuario: "Anda, porfa, ¿puedes guardar por ahí en algún sitio que la variable <loquesea> almacene el valor <patatín-patatán>? Más adelante, cuando vuelvas, yo te pediré si tienes esta información guardada y, si la tienes, te reconoceré y sabré que eres tú, mi amor" (sí, los servidores web son así de ligones con los navegadores).

Los servidores pueden usar así esta información para "recordar" las preferencias del usuario, su login automático y un sinfín de cosas más útiles y beneficiosas para el usuario, y algunas otras que no son tan beneficiosas para el usuario (pero sí para ciertos sitios web, especialmente si venden publicidad o si tienen intenciones algo más siniestras), por ejemplo, para rastrear las preferencias y hábitos de navegación.

Los navegadores o, lo que es lo mismo, los usuarios, sabemos que si nos hacemos los estrechos la página no nos va a dar mucha funcionalidad, así que nos ponemos en modo facilón, nos dejamos querer y accedemos fácilmente a que el servidor web nos penetr meta esos datos sin excesivos reparos. (¿Cuántas veces has "Entendido" que un sitio web utiliza cookies y has consentido? Luego no vayamos con excusas, que así pasa lo que pasa).

El navegador guarda ese paquetito de información, esa cookie, en un fichero pequeño normalmente de texto (también pueden ser binarios) o, algo que se ve cada vez más en las últimas versiones de los navegadores, en bases de datos locales tipo SQLite o similares. En cualquier caso, ficheros en el ordenador del cliente web. O, al menos esto es así en teoría, ya que aquí me surge el primer misterio que mencionaba en la introducción. Pero antes de abordar el misterio, recapitulemos lo que llevamos dicho hasta ahora.


Resumiendo:

- Cookies: pequeños trozos de información que almacena el navegador en ficheros de texto, binarios o BD locales tipo SQLite. Se pueden usar para muchas cosas, una de ellas para almacenar ID de sesión (pero también para rastrear al usuario, conocer preferencias...)

- Sesiones: interacciones entre navegador y servidor web que recuerdan info (estado) del usuario, normalmente usando cookies. La información se almacena en el servidor, ya sea en ficheros de texto (pares nombre:valor), binarios, bases de datos...

3. Vale. ¿Y dónde se almacenan?

3.1 LAS COOKIES

Pues... depende del navegador. En el caso de Firefox, el directorio es el del perfil del usuario, como explican aquí

https://support.mozilla.org/es/kb/perfiles-el-lugar-donde-firefox-almacena-tus-contr

que se encuentra dentro de la carpeta %AppData%\Roaming\Mozilla\Firefox\Profiles

Si sólo tienes un perfil de usuario, éste incluye la palabra 'default' en el nombre de la carpeta. En mi caso:

%AppData%\Roaming\Mozilla\Firefox\Profiles\67bl3kpn.default-1443683694326

y, dentro de esta carpeta, hay un archivo cookies.sqlite


Fig. 1. Archivo SQLite de cookies en Firefox

El contenido de este archivo se puede ver con cualquier visualizador de ficheros SQLite. A mí me gusta mucho el SQLite Expert (edición Personal). Más adelante le echaremos un vistazo.

En el caso de Chrome, la ruta donde se almacena la BD de cookies es

%LOCALAPPDATA%\Google\Chrome\User Data\Default\Cookies

y el fichero se llama cookies a secas, sin extensión. También lo podemos abrir con SQLite Expert o cualquier otro visor.


Si echamos un vistazo a ambos ficheros, veremos cosas parecidas a esto en el caso de Firefox


Fig.2. Fichero de cookies en Firefox 47.0

y esto en el caso de Chrome



Fig.3. Fichero de cookies en Chrome

3.2 LAS SESIONES

La información de la sesión se almacena, como hemos dicho, en el servidor web. En el caso de Apache con PHP, que es el servidor que yo voy a utilizar en este caso, la ubicación de las variables de sesión se almacenan en el directorio que se especifique en php.ini, en el parámetro session.save_path. En mi caso, utilizando un paquete XAMPP integrado, la ruta es C:\xampp\tmp. 

 Fig.4. Configuración de la ruta donde guardar las sesiones

Si miramos en esa carpeta, veremos las sesiones que se han ido creando en los últimos días:




Fig.5. Contenido de la carpeta de almacenamiento de sesiones

4. ¿Me puedes poner un ejemplo?

Sí, será lo mejor. Todo esto puede ser un poco confuso, y la mejor forma de verlo claro es con un ejemplo. Haremos dos páginas PHP que se pasarán información de una a otra utilizando una sesión de usuario, e iremos viendo dónde, cómo y con qué contenido se van creando los ficheros.

Página 1


01 <?php
02 session_start();
03 ?>
04 <!DOCTYPE html>
05 <html lang="es">
06
07 <head>
08 <meta charset="utf-8" />
09 <title>Sesiones - Página 1</title>
10 </head>
11
12 <body>
13 <?php
14 echo 'Estamos en la sesión [' . session_id() . ']<br />'
15 . 'Nombre: ' . session_name() . '<br />';
16
17 //almacenamos algún dato en esa sesión
18 $_SESSION['mes'] = 'junio';
19 $_SESSION['usuario'] = 'pepe';
20 $_SESSION['flor'] = 'amapola';
21
22 echo '<pre>';
23 print_r($_SESSION);
24 echo '</pre>';
25 echo "<br>Ir a <a href='pagina2.php'>la página 2</a> a consultar las variables de sesión"
26
27 ?>
28
29 </body>
30 </html>



Y esta es la página 2


01 <?php
02 session_start();
03 ?>
04 <!DOCTYPE html>
05 <html lang="es">
06
07 <head>
08 <meta charset="utf-8" />
09 <title>Sesiones</title>
10 </head>
11
12 <body>
13 <?php
14 echo 'Estamos en la sesión [' . session_id() . ']<br />Nombre: ' . session_name() . '<br />';
15
16 //recuperamos los valores almacenados en la sesión
17 echo 'Recuperamos el valor mes=' . $_SESSION['mes'] . '<br />';
18 echo 'Recuperamos el valor usuario=' . $_SESSION['usuario'] . '<br />';
19 echo 'Recuperamos el valor flor=' . $_SESSION['flor'] . '<br />';
20
21 echo '<pre>';
22 print_r($_SESSION);
23 echo '</pre>';
24 ?>
25 </body>
26 </html>



Abramos en el navegador la página 1

 Fig. 6. Página 1, creando la sesión y almacenando algunas variables en ella.

Antes de pasar a la página 2, veamos qué ha pasado en los sistemas de archivos del servidor y del cliente.

En el servidor

Fig. 7. Fichero de sesión en el sistema de archivos del servidor web

como se ve, el nombre del archivo coincide con el session_id que hemos visto en la página. Si abrimos ese fichero, su contenido es:


Fig. 8. Contenido del fichero de sesión con las variables de la misma

Hmmm... Interesante. Vemos que junto a cada variable aparece un tipo de datos (s=string), su tamaño (5) y su valor ("junio"). Las distintas variables de sesión se separan con pipelines.


Pero es a la hora de ver qué ha pasado con las cookies en el cliente cuando aparece el primer "misterio". En teoría, las cookies se almacenan en la BD SQLite que hemos dicho. Pero, cuando muestro las cookies ordenadas por fecha de acceso, no encuentro la cookie de nombre PHPSESSID, que es el nombre que me devuelve la función session_name(). Esto es lo que consigo ver cuando accedo a la BD y filtro por el dominio 'localhost':

Fig. 9. Contenido del fichero de cookies para el dominio 'localhost' en Firefox. 
¿Y la cookie PHPSESSID?

Como tras muchos intentos no he conseguido encontrar esta información en la BD, al final he instalado el plugin Firebug en Firefox, que permite visualizar las cookies y ahí sí que puedo ver la cookie que me interesa, como se puede ver un poco más arriba en la figura 6. También si abro la página 2 con el Firebug activado (F12) puedo ver que la cookie de sesión PHPSESSID es la misma

Fig. 10. Página 2 mostrando la cookie de sesión y las variables asociadas, junto a sus valores

Aún no he conseguido averiguar dónde se almacena la cookie PHPSESSID, pero es obvio que el navegador la está guardando en algún sitio y a mí me gustaría saber dónde. Vale que con el Firebug puedo inspeccionar la cookie, lo cual me soluciona la papeleta por el momento, pero me quedo un poco mosca con no haber podido encontrar la ubicación exacta de la cookie en el disco.

¿Alguien tiene alguna idea?

martes, 10 de mayo de 2016

Estilos CSS alternativos

A veces nos puede interesar ofrecer dos páginas de estilos CSS diferentes para una página web. Por ejemplo, una versión normal y otra aumentando el contraste, para facilitar la accesibilidad de personas con problemas visuales. O simplemente por ofrecer dos temas diferentes al usuario, basados en gustos por determinados colores o fuentes, disposición de elementos en la página...

Veamos cómo hacer esto.

Definiremos dos hojas, estilo1.css y estilo2.css con los siguientes contenidos:

Fichero 1
/* estilo 1, normal */
body {
    font-family: verdana,helvetica;
    color: #0000FF; /* texto azul */
    background-color: #f8e8a0; /* fondo amarillo */
    font-size: 16px;
}


Fichero 2
/* estilo 2, alto contraste, letras grandes */
body {
    font-family: verdana,helvetica;
    color: #ffffff; /* texto blanco */
    background-color: #000000; /* fondo negro */
    font-size: 36px;
}


Y ahora hacemos nuestra página, tal que así

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>sin título</title>
<link href="estilo1.css" rel="stylesheet" type="text/css" title="Estilo 1">
<link href="estilo2.css" rel="stylesheet" type="text/css" title="Estilo 2">
</head>

<body>

<h1>Elija una de las siguientes opciones</h1>
<ol>
    <li>Opción primera</li>
    <li>Opción segunda</li>
    <li>Opción tercera</li>
</ol>
</body>

</html>


Si abrimos ahora la página en el navegador, la veremos con el primer estilo aplicado:



Fig 1. Página mostrada con el Estilo 1

Y en el menú Ver / Estilos podemos elegir cambiar a otro estilo

Fig 2. Elegir un estilo diferente en el navegador


Y veríamos el resultado que hemos definido de alto contraste y letras grandes, tal que así:

Fig 3. Página mostrada con el Estilo 2

ALGUNAS EXPLICACIONES

En los atributos "title" hemos puesto los valores "Estilo 1" y "Estilo 2". Si no hubiéramos definido el atributo "title" en alguno de los estilos esto significaría que el estilo es permanente. Esto significa que se aplica siempre, independientemente de otros estilos que se eligieran por el usuario. Si hay enlaces a varias páginas CSS sin "title", se aplicarían todas ellas.

Al incluir el atributo "title", lo que estamos haciendo es indicar que esos estilos son preferidos. Esto significa que se aplican por defecto (en nuestro caso, el "Estilo 1"), pero si el usuario selecciona otra(s) página(s) con título diferente, dejarían de aplicarse las primeras y pasarían a aplicarse todas las que tuvieran el otro estilo (en nuestro caso, el "Estilo 2"). Si dos o más páginas tienen el mismo título, entonces se aplican todas ellas al elegir el estilo correspondiente en el menú Ver del navegador.

Normalmente, hasta aquí suele ser suficiente. Pero el estándar HTML también define otro valor posible para el atributo "rel"

rel="alternate stylesheet"

Aunque no todos los navegadores implementan esto. Básicamente, está indicando que hay otros estilos alternativos, pero, al contrario que los estilos preferidos (que se aplican por defecto, al menos el primero de ellos), los estilos alternativos no se aplican por defecto, sino sólo en el caso de que el usuario lo seleccione explícitamente.

La verdad es que con utilizar estilos con títulos ya tendríamos un sistema de estilos alternativos, así que en general esta etiqueta me parece innecesaria para solucionar situaciones como la planteada al principio. Seguramente pueda idearse algún caso donde la diferencia sea significativa, pero en general no lo considero algo crítico, al menos para casos típicos.

Si queréis unas explicaciones más detalladas, echadle un vistazo a esta página, que está muy bien explicado todo esto.

viernes, 15 de abril de 2016

To REST or not to REST

Harto de encontrarme por todos lados en los últimos tiempos las palabras REST o RESTful cuando se habla de aplicaciones web, servicios web y temas relacionados, y no saber qué significan, me he decidido dedicar un rato (un par de horas) a aprender qué quiere decir esto.

Aquí va mi resumen personal para leer en cinco minutos, al que volveré dentro de un tiempo cuando se me haya olvidado, y espero que me sirva de refresco. Quien quiera más detalles, abajo encontrará algunos enlaces. Básicamente, lo que resumo aquí se basa en un 70-80% en el primer enlace, que es el que más útil me ha parecido de los que he encontrado hasta ahora.

Fig. 1. REST es un conjunto de ideas de las que destacan 4 tomates principios. 
Imagen de Softeis - Trabajo propio, CC BY-SA 3.0, https://commons.wikimedia.org/w/index.php?curid=143779
 

1. Qué es REST

Un conjunto de principios de arquitectura para el desarrollo de aplicaciones y servicios web.
Surge en oposición a SOAP y WSDL, que son más complicados.
Grandes proveedores 2.0 usan APIs RESTful (Google, Facebook...)

El énfasis se pone en los recursos (¿objetos?) y su transferencia por HTTP.

2. Principios básicos

2.1. Uso explícito de métodos HTTP
Se deben utilizar como se definieron en la especificación original de HTTP 1.1 (RFC 2616). Esto significa:

- GET para consultas
- POST para altas
- PUT para actualizaciones
- DELETE para ...¿lo adivinas?

Usar un GET para insertar o actualizar un registro en una BD es posible pero no es acorde con los principios RESTful.

2.2. Protocolo cliente/servidor sin estado
Cada petición HTTP debe contener toda la información necesaria.

Esto debe facilitar la escalabilidad, no se debe hacer que un servidor recupere el contexto si, por ejemplo, tiene que redirigir la petición a otro servidor para balanceo de carga.

Se transfiere el problema de mantener el estado al cliente.

Aunque a la hora de la práctica, aplicaciones que se definen como RESTful suelen usar cookies y otras formas de mantener el estado. ??

2.3. Exponer URIs como directorios y ficheros
Las URIs son así más intuitivas. Por ejemplo: http://www.dominio.com/informes/2016/04 nos podría devolver los informes de abril de 2016. Es una especie de sintaxis universal.

También se oculta la tecnología (no se ven extensiones de archivos, como ocurre en casos como  http://www.dominio.com/informes/recuperar_informes.php donde se ve que se utiliza PHP). Así se puede cambiar la tecnología subyacente sin cambiar la URI.

Parece que este es el principio más REST de los cuatro.

4. Los recursos se devuelven en XML, HTML o JSON
Por ejemplo, un registro de una BD. Estos recursos pueden contener enlaces (URIs) a otros recursos.

3. Ideas importantes
Al exponer los recursos mediante una API RESTful, se facilita la integración de aplicaciones de diferente naturaleza y tecnologías. Se puede proporcionar una base de servicios REST que pueden utilizar otras aplicaciones más complejas para construir sobre ellos.

No todas las implementaciones públicas que se definen como REST son RESTful. Parece que el principio predominante que sí se suele respetar es el de la URI uniforme (principio 3), como podemos ver, por ejemplo, aquí.

También he detectado un poco de bombo en torno al término REST, pero esto es inevitable en esta profesión, donde los nuevos términos se utilizan a menudo como el equivalente tecnológico de cojonudo.

4. Más info
  • RESTful Web services: The basics
http://www.ibm.com/developerworks/webservices/library/ws-restful/index.html?ca=dgr-jw22RESTfulBasics&S_Tact=105AGX59&S_CMP=GRsitejw22

  • Versión en español del artículo anterior
http://www.dosideas.com/noticias/java/314-introduccion-a-los-servicios-web-restful.html

  • Artículo en la Wikipedia en español (mejor leerlo después de leer el primer enlace)
https://es.wikipedia.org/wiki/Representational_State_Transfer

  • Servicios que son "accidentalmente" RESTful
http://www.markbaker.ca/blog/2005/04/14/#2005-04-amazon-next

He leído también detalles en otras páginas, pero son cosas más puntuales, y nada que no podáis superar con una simple búsqueda.

jueves, 14 de abril de 2016

Espacio en disco en formato legible para las personas (linux)

Aunque hay muchas herramientas gráficas para ver el espacio disponible en disco, uno siempre acaba volviendo de forma recurrente al útil comando df, que se puede usar tecleando sin más

$ df

y produce una salida tal que así

Fig 1. Salida del comando df


Por pequeño que sea un disco, siempre salen números muy grandes, ya que da el tamaño en bloques de 1K. Resulta un poco difícil de decir, a bote pronto, cuántos gigas libres te quedan.

Pero claro, normalmente nos gusta más leer el tamaño en megas y gigas. Para esto, viene muy bien la opción -h (legible por Humanos) del comando df.

$ df -h


Fig 2. Salida del comando df con la opción -h (humanos)



Y ahora sí que se puede leer bien.

 Como curiosidad, he marcado la tercerra línea para comparar cómo se ve esa misma información con el explorador Nautilus de Ubuntu, mostrando las propiedades del equipo.

Fig. 3. Ver el espacio en disco con Nautilus

Y se ve algo así

Fig. 4. Salida de Nautilus (tarda un buen rato)

Una curiosidad es que Nautilus y el comando df no muestran exactamente el mismo espacio usado (10,6 GB en un caso y 10G en el otro). Curioso.

Por cierto, me estoy quedando sin espacio en la partición /boot. Habrá que hacer algo.




miércoles, 9 de marzo de 2016

Servidores web más populares en el IBEX 35

Hace poco vimos cómo analizar las cabeceras en las comunicaciones HTTP con cURL [aquí].

Entre otras cosas, vimos el valor de la propiedad "Server", que nos informaba del servidor web y algunas tecnologías que podían estar disponibles en el servidor (PHP, ASP...).

Pues bien, hace unos días me surgió la pregunta de qué servidores web son los más populares entre las "empresas grandes". Veamos cómo podemos usar cURL y los filtros de Linux (lo mismo se puede hacer en Windows) para hacer una pequeña estadística de esto.

Primero, como "empresas grandes" he considerado las que forman parte del IBEX 35.

Si queremos ver lo que nos devuelven un par de ellas, podemos escribir algo así:

curl -I --url www.abertis.com --url www.endesa.com

Y obtendríamos esta respuesta


Fig. 1. Cabeceras de dos sitios web

Podemos filtrar sólo aquellas cabeceras que tengan la cadena "Server:"

curl -I --url www.abertis.com --url www.endesa.com |grep "Server:"


Fig. 2. Filtrando para quedarnos exclusivamente con la cabecera "Server:"

Como se puede ver, al usar cURL en pipelines nos va mostrando una estadística con los porcentajes de descargas, un medidor de progreso (cURL lo llama "Progress Meter"). De momento lo ignoraremos.

En el ejemplo anterior he puesto únicamente dos URLs, pero como me interesan las 35 URLs de las empresas del IBEX 35, lo que haré será poner estas URLs en un fichero y usar la opción -K de cURL para decirle que tome de allí las URLs.


Fig. 3. Fichero con las URLs de las empresas del IBEX 35.

Ya podemos invocar a cURL con ese fichero:

curl -I -K urls_ibex35.txt |grep "Server:"


Fig. 4. Vamos viendo los servidores, pero el medidor de progreso nos ensucia la salida

Con la opción -s (silent) podemos hacer que desaparezca el medidor de progreso.

curl -I -K urls_ibex35.txt -s |grep "Server:"


Fig. 5. La cabecera "Server" sin el medidor de progreso

Vale, ya estamos cerca del final. Podemos ver unas cuantas cosas interesantes. En primer lugar, no todos los servidores devuelven la cabecera "Server". De hecho, hay tan sólo 26 resultados, lo cual quiere decir que más de la cuarta parte de los administradores web de estas empresas (9 servidores) han decidido ocultar la información acerca del servidor web que utilizan. Además, uno de ellos devuelve una cadena en blanco, lo cual es lo mismo que ocultarlo. Total, 10 de 35 no lo indican, casi un 29%. Lo cual quiere decir que el otro 71% ven con buenos ojos publicar esta información, aunque con diferentes niveles de detalle. Y todo esto, asumiendo que no hayan falseado esta cadena para devolver un valor erróneo, cosa que también cae dentro de lo posible (aunque lo considero poco probable, la verdad).

Por último, en vez de contar a mano, hagamos un poco de "pipelining".

curl -I -K urls_ibex35.txt -s |grep "Server:" |sort |uniq -c |sort -nr


Fig. 6. Agrupando por Servidor web

Como vemos, el ganador es...(redoble de tambores...)... ¡APACHE!

Apache está disponible en 12 de los 26 sitios web que publican la información, o sea, un 46%. Una gran diferencia con respecto a la segunda posición, ocupada por diferentes versiones del servidor de Microsoft, IIS, con 4 sitios (un 15%) y en tercera posición hay un (sorprendente para mí, pues no lo conocía) servidor llamado AkamaiGHost, que tienen 3 de las 26 empresas (casi un 12%).

Bueno, me parecen unos resultados interesantes de conocer. Además, no puede uno evitar pensar enseguida en extender esta estadística a otras empresas, universidades, organismos oficiales, gobiernos... Pero eso lo dejaremos para otra ocasión.



¿Y tu organización, qué servidor web utiliza? ¿Publica su nombre, versión, tecnologías asociadas...?

lunes, 22 de febrero de 2016

Convertir a mayúsculas o minúsculas con el comando dd

El comando dd permite, entre otras cosas, hacer copias de un fichero aplicando conversiones en el proceso. Dos conversiones sencillas que pueden llevarse a cabo con ficheros de texto son pasar todo a mayúsculas o a minúsculas.

Para ello,hay que teclear algo así

dd if=a.txt of=b.txt conv=ucase

Donde if significa "input file" y of significa "output file".

Si cambiamos "ucase" por "lcase" transformaremos todo el texto en minúsculas.


Atentos a los caracteres con tildes.

lunes, 8 de febrero de 2016

Configurar la cabecera Server en el servidor web Apache 2 (Ubuntu y Windows)

En la anterior entrada hablé sobre las cabeceras que envían los servidores web y, en particular, sobre la cabecera "Server". Pero, ¿dónde se configura esta cabecera en nuestro servidor web, en particular en el caso de Apache?


Esto se determina en la directiva ServerToken en el fichero de configuración (apache2.conf en Ubuntu, httpd.conf en Windows), o bien en algún fichero incluido de manera transitiva en él. 

Esta directiva puede tomar los valores Prod, Major, Minor, Minimal, OS, Full, enviando cada vez más información detallada en ese orden. Si no se especifica, se asume el valor Full. Como se explica en la documentación oficial de Apache (http://httpd.apache.org/docs/2.0/es/mod/core.html#servertokens):



Fig. 1. Valores posibles para la directiva ServerTokens

Vale. Entonces ¿dónde está el fichero de configuración?

EN UBUNTU

Ubicación del fichero de configuración:  /etc/apache2/apache2.conf

Fig. 2. Cuando no está establecida la directiva, se devuelve la información "Full"

Puede verse que la directiva existe, a modo de ejemplo (pero sin ser efectiva), en un fichero de configuración disponible en el subdirectorio conf-available, pero para que esta entrada fuera efectiva tendríamos que incluir una referencia a él en el fichero principal /etc/apache2/apache2.conf, lo que ahora mismo no está ocurriendo. 

Como podemos observar, el servidor ahora mismo está devolviendo la cadena Apache/2.4.12 (Ubuntu)

En nuestro caso, nos limitaremos a incluir una línea con la directiva establecida al valor Prod (esto significa Producción, entorno desde el que se debería enviar la mínima información como medida de precaución).
Modificamos el fichero, reiniciamos el servidor (esto es necesario) y preguntamos ahora por las cabeceras

Fig. 3. Tras la modificación, la cabecera ha cambiado el valor enviado

Como se ve, ahora se está devolviendo el valor Apache a secas.


EN WINDOWS

Lo único que cambia en Windows es el fichero de configuración, que en este caso es apache\conf\httpd.conf o, como ocurre en mi caso, puedo cambiarlo en el fichero extra\httpd-default.conf, que es incluido en el primero mediante una directiva

# Various default settings
Include "conf/extra/httpd-default.conf"


Pues nada, ya sabemos cómo configurar la cabecera Server. Cuidado con lo que enviáis a vuestros clientes.

viernes, 15 de enero de 2016

Cabeceras en comunicaciones HTTP con cURL

Aparte del contenido HTML que se transfiere durante una consulta a un sitio web, también se transmite otra información en forma de cabeceras, que no son más que pares del tipo "valor: resultado". Estas cabeceras normalmente no se muestran al usuario que está visitando el sitio web, pero el navegador sí que las recibe, pudiendo ignorar algunas y utilizar otras, según le interese. Es interesante echar un vistazo a esta información que, en principio, no se ve.

Echando un vistazo a lo que no se ve
Imagen: Lector con lupa, Lesser Ury. Fuente: wikipedia.

Con curl podemos ver las cabeceras que nos está enviando un servidor web usando la opción -I. Por ejemplo, para ver las cabeceras que envía el servidor de la RAE:

Fig. 1. Cabeceras del servidor web de la RAE

Como se puede ver en la Fig. 1, una de las cabeceras se llama Server. En el caso de la RAE, el valor es Apache, lo cual nos puede llevar a pensar que la RAE utiliza como servidor web Apache.

Digo "nos puede llevar a pensar" porque esto no es necesariamente así. Al fin y al cabo, el administrador del servidor web puede configurarlo para que devuelva en esa cabecera cualquier cadena de texto que quiera.

Algunos administradores, por motivos de seguridad, prefieren no informar a los visitantes del servidor web que utilizan, por ponérselo un paso más difícil a los hackers (esta técnica de ocultación tampoco contribuye demasiado, la verdad, pero algo es algo). Otros dejan tan sólo el nombre del servidor web y eliminan los números de versión (informar del nombre y versión del servidor web suele ser la opción configurada por omisión). Así, al menos, intentan dificultar que un hacker pueda explotar alguna vulnerabilidad conocida que sea específica de esa versión del servidor web o del motor de PHP, de ASP...

En este caso, podemos comprobar que el administrador del servidor web de la RAE ha eliminado la versión de la cadena que describe el servidor Apache (suponiendo que sea ese el servidor real, lo cual asumiremos por el momento).

Otras veces, en las cabeceras podemos encontrar si el servidor está utilizando PHP, ASP u otras tecnologías, incluyendo el número de versión:

Fig. 2. Cabeceras del servidor web de Endesa indicando que utiliza IIS en 
su versión 7.0 y ASP.NET (sin indicar número de versión)

Pero recordad, dado que lo que se devuelve es una cadena de texto libremente modificable, también puede darse el caso, siendo un poco más retorcido, de que se esté informando de un número de versión erróneo, a fin de despistar al visitante. Así que no conviene, como decía Microsoft en un anuncio, asumir que "por llamarse Mercedes, eso signifique que sea un coche".