Posts Tagged ‘ gae

Google App Engine como tu CDN dinámico

Debido a un proyecto he necesitado estudiar diferentes alternativas como CDN, y me decidí a estudiar GAE como una posibilidad.

Mi intención era subir contenidos multimedia (imágen, vídeo y sonido) dinámicamente y restringir el acceso a dicha información.

Para la subida hago un simple POST con la información y para la seguridad ideé un sistema basado en slots el cual explicaré más abajo.

Al ajo, vamos con el modelo:

# coding=utf-8
 
from google.appengine.ext import db
 
class Slot(db.Model):
    id = db.IntegerProperty(required=True)
    hash = db.StringProperty(multiline=False,required=True)
 
class File(db.Model):
    id = db.IntegerProperty(required=True)
    content = db.BlobProperty(required=True)

Como vemos, la clase File encapsula el fichero en sí, con un identificador y un BlobProperty que almacenará los datos.

La clase Slot encapsula un slot temporal que se ha abierto a ese fichero, consiste en un hash utilizado para cargar el fichero mediante la URL y el identificador del fichero en cuestión.

Las operaciones para acceder a un fichero en el CDN serían las siguientes;

  1. El cliente quiere obtener un fichero
  2. El servidor web (no AppEngine) hace la petición a AppEngine obteniendo el hash pertinente.
  3. El servidor web lleva este hash como URL al cliente, de la forma http://xxxx.appspot.com/[hash]
  4. El cliente obtiene el fichero e inmediantamente el hash es borrado.

De este modo, utilizo dos URLs:

  • /[string]: Siendo string un hash (previamente negociado) mediante GET obtengo el fichero y siendo string un identificador mediante POST subo el fichero concreto
  • /slot/[id]: Siendo id un identificador, obtendría un slot asociado a él.

De ambas funcionalidades la única pública es cargar el fichero con el hash, para las otras dos es necesario subir una contraseña previamente establecida.

Simple, ahora echemos un vistazo al script de AppEngine que gestiona la aplicación:

# coding=utf-8
 
from google.appengine.ext import webapp
from google.appengine.ext.webapp.util import run_wsgi_app
from django.utils import simplejson
import hashlib
 
from models import *
 
SECRET = "secreto"
 
class getSlot(webapp.RequestHandler):
    def post(self,id):
        if not id.isdigit():
            self.error(400)
 
        if self.request.get("passwd")==SECRET:
            hash = hashlib.sha256().hexdigest()
            Slot(id=int(id),hash=hash).put()
 
            self.response.headers['Content-Type'] = 'text/plain'
            self.response.out.write(simplejson.dumps(dict(slot=hash)))
        else:
            self.error(500)
 
class FileHandler(webapp.RequestHandler):
    def get(self,hash):
        query = Slot.filter("hash = ",hash)
 
        if query.count()==0:
            self.error(404)
            return
 
        # Get the slot
        slot = query.get()
 
 
        file = File.filter("id = ",slot.id);
        # Get the mimetype
        self.response.headers['Content-Type'] = 'image/jpeg'
        self.response.out.write(file.content)
 
        # Remove the slot
        slot.delete()
 
    def post(self,id):
        if not id.isdigit():
            self.error(400)
 
        if self.request.get("passwd")==SECRET:
 
            File(id=id,content=self.request.get("file")).put()
            self.response.headers['Content-Type'] = 'text/plain'
 
            self.response.out.write("ok")
        else:
            self.error(500)
 
 
application = webapp.WSGIApplication(
                                     [(r'/slot/(.+)', getSlot),
                                      (r'/(.+)', FileHandler),
                                     ],
                                     debug=True)
 
def main():
  run_wsgi_app(application)
 
if __name__ == "__main__":
  main()

Este script hace lo descrito anteriormente, por un lado nos permite subir ficheros al servidor, obtener el hash y acceder a dicho hash publicamente una sóla vez.

Las operaciones privadas se verifican mediante una contraseña que tenemos que establecer previamente, podríamos haber elegido cualquier otro método, pero este es el más simple.

Veamos el script que realiza las operaciones de subida;

# coding=utf-8
from poster.encode import multipart_encode
from poster.streaminghttp import register_openers
import urllib2
 
URL = "http://localhost:8080/"
 
def open_slot(id,passwd):
 
    register_openers()
 
    datagen, headers = multipart_encode({"passwd":passwd})
 
    request = urllib2.Request(URL+"slot/"+str(id), datagen, headers)
 
    print urllib2.urlopen(request).read()
 
def upload(id,file,passwd):
 
    register_openers()
 
 
    datagen, headers = multipart_encode({"file": open(file, "rb"),"passwd":passwd})
 
    request = urllib2.Request(URL+str(id), datagen, headers)
 
    print urllib2.urlopen(request).read()

Previamente necesitamos instalar la biblioteca poster de Python.
Este script mediante las funciones upload() nos permite subir un fichero con una ID determinada, el nombre del mismo y la contraseña para identificarnos.
Hecho esto, podremos abrir un slot asociado a él mediante la función open_slot().

Ahora para acceder al fichero, sólo tendremos que ir a la dirección http://[url principal]/[hash]

Resulta interesante este tipo de ejercicios con diferentes tecnologías para ver las utilidades que nos ofrecen.
Finalmente descarté el invento debido a las restricciones que tiene impuestas, por un lado, los campos Blob no pueden tener más de 1MB y aunque se podría aumentar indexando varios (consumiendo un riquísimo tiempo de CPU) los campos del POST no pueden ocupar más de 10MB.

Aún así, resulta interesante este sistema y porque no, su posible aplicación a otros proyectos.

Un gran número de parejas jóvenes que se enfrentan a varios problemas de salud, tales personas pueden comprar medicamentos en línea recta sin orden. Antibióticos de penicilina muy populares que combaten las bacterias. Estos remedios no tratan una infección viral por ejemplo un resfriado común. Vamos a hablar por teléfono de numerosas drogas existe. Kamagra es un remedio usado para tratar de varias quejas. ¿Qué sabes sobre “Comprar Kamagra Oral Jelly“? Actualmente muchos hombres buscan la frase exacta “comprar kamagra 100mg” en Internet. (Leer más “Kamagra Oral Jelly“). Debido a que algunos de los problemas sexuales son emergencias médicas, es bueno conocer los síntomas. Ciertas personas que usan este medicamento generalmente no tienen efectos secundarios graves Kamagra. El farmacéutico necesita resolver qué dosis es la mejor en su caso. Si el medicamento se usa según sea necesario, es poco probable que esté en un horario de dosificación.

Django 1.2.3 + Google App Engine

Debido al proyecto Georemidme, me he tenido que empapar del funcionamiento de Google App Engine, algo que es relativamente simple.

Google da soporte para Django 0.9.6 y nosotros queremos usar la versión 1.2.3 (o cualquier otra), ¿cómo lo hacemos?

Antes, quiero hacer unas aclaraciones respecto a los modelos en Google App Engine; Google da soporte para cualquier módulo Python que corra en Python 2.5, pero el almacenamiento de los modelos no es una base de datos clásica relacional si no que utiliza sus propios sistemas.

Como consecuencia no podremos utilizar los modelos nativos Django (si se podría, utilizando parches para que los emulen), así que las aplicaciones y middlewares incluidas con Django que utilizan modelos no las podemos usar.

Nosotros hemos preferido no utilizar ningún parche de modo que tengamos que aprender el API de almacenamiento de Google 🙂

Hechas estas aclaraciones vamos a pasar a describir paso por paso como utilizar nuestra propia versión de Django con Google App Engine.

En el directorio raiz del SDK de GAE creamos nuestro proyecto Django. Como he comentado, tendremos que desactivar todas las aplicaciones y middleware que utilicen el modelo de Django (auth, admin, sessions, messages, etc).

En el directorio del proyecto, creamos el siguiente fichero llamado main.py:

import logging, os, sys
 
# Google App Engine imports.
from google.appengine.ext.webapp import util
 
# Remove the standard version of Django.
for k in [k for k in sys.modules if k.startswith('django')]:
  del sys.modules[k]
 
# Force sys.path to have our own directory first, in case we want to import
# from it.
sys.path.insert(0, 'django.zip')
sys.path.insert(0,os.path.abspath(os.path.dirname(__file__)))
 
# Must set this env var *before* importing any part of Django
os.environ['DJANGO_SETTINGS_MODULE'] = 'settings'
 
import django
 
import django.core.handlers.wsgi
import django.core.signals
import django.db
import django.dispatch.dispatcher
 
def log_exception(*args, **kwds):
 logging.exception('Exception in request:')
 
def main():
  # Create a Django application for WSGI.
  application = django.core.handlers.wsgi.WSGIHandler()
 
  # Run the WSGI CGI handler with that application.
  util.run_wsgi_app(application)
 
if __name__ == '__main__':
  main()

Como vemos, el fichero incluye un tal django.zip, esta será nuestra versión de Django comprimida que estará en el mismo directorio, y sí, hay una biblioteca que permite cargar módulos comprimidos. Supongo que para importar cualquier módulo será igual.

El fichero app.yaml podría ser:

application: appquesea
version: 1
runtime: python
api_version: 1

handlers:
- url: /static
  static_dir: static

- url: /.*
  script: main.py

Donde static/ serán los contenidos estáticos.

Por último señalar que me ha estado dando problemas nombrar los módulos con nombredeaplicacion.loquesea, que si os da problemas quitad el nombredeaplicacion.

Un gran número de parejas jóvenes que se enfrentan a varios problemas de salud, tales personas pueden comprar medicamentos en línea de investigación sin orden. Antibióticos de penicilina muy populares que combaten las bacterias. Estos remedios no tratan una infección viral por ejemplo un resfriado común. Vamos a hablar de numerosas drogas existe. Kamagra es un remedio usado para tratar varias quejas. ¿Qué sabes sobre “Comprar Kamagra Oral Jelly“? Actualmente muchos hombres buscan la frase exacta “comprar kamagra 100mg” en Internet. (Leer más “Kamagra Oral Jelly“). Debido a que algunos de los problemas sexuales son emergencias médicas, es bueno conocer los síntomas. Ciertas personas que usan este medicamento generalmente no tienen efectos secundarios graves Kamagra. El farmacéutico necesita resolver qué dosis es la mejor en su caso. Si el medicamento se usa según sea necesario, es poco probable que esté en un horario de dosificación.