Showing posts with label gae. Show all posts
Showing posts with label gae. Show all posts

Friday, February 4, 2011

Enabling SSL with Python on Cygwin (secures Google App Engine's appcfg.py tool)

I prefer to run the dev_appserver through Cygwin, which works great, but appcfg complains:
WARNING appengine_rpc.py:399 ssl module not found. Without the ssl module, the identity of the remote host cannot be verified, and connections may NOT be secure. To fix this, please install the ssl module from http://pypi.python.org/pypi/ssl
I tried to fix this last summer, but didn't get anywhere on Cygwin and gave up after a few hours.  Instead,  I was able to install the ssl module on my Windows installation of python (not Cygwin) and have been using the App Engine Launcher (Windows client) to deploy my apps.

Today I decided to give try again on Cygwin.  To my surprise, the solution turned out to be very simple, but I only realized this after I messed up my Cygwin installation and had to run setup.exe about fifty times with different settings before I fixed it.

So here is the process:

1. download the ssl-1.15.tar.gz from http://pypi.python.org/pypi/ssl and extract the archive
2. > cd ssl-1.15
3. > python setup.py build


If the build fails, complaining about some OpenSSL-related stuff missing, just run Cygwin's setup.exe and install the openssl-devel package (make sure you get the same version as your libopenssl package, which you will also need, by the way).  Now the build should succeed.

4. > python setup.py install

That should do the trick.  

Be careful: do not run the install command until the build command runs successfully and until you make sure that you have the same version of the libopenssl and openssl-devel packages in your Cygwin installation.

Wednesday, December 29, 2010

Setting cookies from Google App Engine

I was disappointed to realize that the google.appengine.ext.webapp.Response class, while in many ways similar to the WebOb Response class, doesn't support the latter's set_cookie method.

I thought about using python's Cookie module directly, but but the WebOb method looked much more convenient.  So I decided the smartest thing to do was to just get the source code for the cookie-related operations from WebOb and adapt it to Google App Engine.  Feel free to use this little hack in your projects.

Example Usage:
   class FooHandler(webapp.RequestHandler):  
    def get(self):  
     cookies = Cookies(self.response)  
     cookies.add_cookie('foo', 'bar', max_age=360, path='/',
                        domain='example.org', secure=True) 


Source Code (cookies.py):
 from Cookie import BaseCookie  
 from datetime import datetime, date, timedelta  
 import time  
   
 # Implements WebOb's set_cookie, unset_cookie, and delete_cookie methods which GAE's webapp framework is missing  
 # modified from original WebOB code found at http://python-webob.sourcearchive.com/documentation/0.9.8-1/response_8py-source.html  
   
 # author: Alex Epshteyn ( http://myprogblog.blogspot.com/ )  
   
 # Original license from WebOb framework, which also applies to this file:  
   
 # Copyright (c) 2007 Ian Bicking and Contributors  
 # Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:  
 # The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.  
 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.  
   
 class Cookies(object):  
  """  
  Wraps the 'headers' field of a google.appengine.ext.webapp.Response instance with cookie methods probided for a WebOb response object.  
  Example usage:  
   class FooHandler(webapp.RequestHandler):  
    def get(self):  
     cookies = Cookies(self.response)  
     cookies.add_cookie('foo', 'bar')  
  """  
   
  def __init__(self, response):  
   """  
   Takes a GAE webapp Response object (i.e. the value of the 'response' field of a google.appengine.ext.webapp.RequestHandler instance)  
   """  
   self.headers = response.headers  
   
  def set_cookie(self, key, value='', max_age=None,  
          path='/', domain=None, secure=None, httponly=False,  
          version=None, comment=None, expires=None, overwrite=False):  
    """  
    Set (add) a cookie for the response  
    """  
    if isinstance(value, unicode) and self.charset is not None:  
      value = '"%s"' % value.encode(self.charset)  
    if overwrite:  
      self.unset_cookie(key, strict=False)  
    cookies = BaseCookie()  
    cookies[key] = value  
    if isinstance(max_age, timedelta):  
      max_age = max_age.seconds + max_age.days*24*60*60  
    if max_age is not None and expires is None:  
      expires = datetime.utcnow() + timedelta(seconds=max_age)  
    if isinstance(expires, timedelta):  
      expires = datetime.utcnow() + expires  
    if isinstance(expires, datetime):  
      expires = '"'+_serialize_cookie_date(expires)+'"'  
    for var_name, var_value in [  
      ('max-age', max_age),  
      ('path', path),  
      ('domain', domain),  
      ('secure', secure),  
      ('HttpOnly', httponly),  
      ('version', version),  
      ('comment', comment),  
      ('expires', expires),  
    ]:  
      if var_value is not None and var_value is not False:  
        cookies[key][var_name] = str(var_value)  
    self._add_cookie(cookies)  
   
  def _add_cookie(self, cookie):  
    if not isinstance(cookie, str):  
      cookie = cookie.output(header='').lstrip()  
      if cookie.endswith(';'):  
        # Python 2.4 adds a trailing ; to the end, strip it to be  
        # consistent with 2.5  
        cookie = cookie[:-1]  
    if cookie:  
      self.headers.add_header('Set-Cookie', cookie)  
   
   
  def delete_cookie(self, key, path='/', domain=None):  
    """  
    Delete a cookie from the client. Note that path and domain must match  
    how the cookie was originally set.  
   
    This sets the cookie to the empty string, and max_age=0 so  
    that it should expire immediately.  
    """  
    self.set_cookie(key, '', path=path, domain=domain,  
            max_age=0, expires=timedelta(days=-5))  
   
  def unset_cookie(self, key, strict=True):  
    """  
    Unset a cookie with the given name (remove it from the  
    response). If there are multiple cookies (e.g., two cookies  
    with the same name and different paths or domains), all such  
    cookies will be deleted.  
    """  
    existing = self.headers.get_all('Set-Cookie')  
    if not existing:  
      if not strict:  
        return  
      raise KeyError("No cookies at all have been set")  
    del self.headers['Set-Cookie']  
    found = False  
    for header in existing:  
      cookies = BaseCookie()  
      cookies.load(header)  
      if key in cookies:  
        found = True  
        del cookies[key]  
        self._add_cookie(cookies)  
      else:  
        # this branching is required because Cookie.Morsel.output()  
        # strips quotes from expires= parameter, so better use  
        # it as is, if it hasn't changed  
        self._add_cookie(header)  
    if strict and not found:  
      raise KeyError(  
        "No cookie has been set with the name %r" % key)  
   
 # this function is from WebOb's datetime_utils.py (http://python-webob.sourcearchive.com/documentation/0.9.8-1/datetime__utils_8py-source.html)  
 def _serialize_cookie_date(dt):  
   if dt is None:  
     return None  
   if isinstance(dt, unicode):  
     dt = dt.encode('ascii')  
   if isinstance(dt, timedelta):  
     dt = datetime.now() + dt  
   if isinstance(dt, (datetime, date)):  
     dt = dt.timetuple()  
   return time.strftime('%a, %d-%b-%Y %H:%M:%S GMT', dt)