Showing posts with label mod_wsgi. Show all posts
Showing posts with label mod_wsgi. Show all posts

2014-08-28

Pyramid Single File Tasks Tutorial runs under mod_wsgi

I try to make the Pyramid Single File Tasks Tutorial runs under mod_wsgi, and figure that two changes are needed to make it runs both standalone (http://localhost:6543) and under mod_wsgi (http://localhost/task).

I would like to share these two changes:

[tasks.py]

FROM:
if __name__ == '__main__':
    # configuration settings
    settings = {}
    settings['reload_all'] = True
    settings['debug_all'] = True
    settings['mako.directories'] = os.path.join(here, 'templates')
    settings['db'] = os.path.join(here, 'tasks.db')
    # session factory
    session_factory = UnencryptedCookieSessionFactoryConfig('itsaseekreet')
    # configuration setup
    config = Configurator(settings=settings, session_factory=session_factory)
    # routes setup
    config.add_route('list', '/')
    config.add_route('new', '/new')
    config.add_route('close', '/close/{id}')
    # static view setup
    config.add_static_view('static', os.path.join(here, 'static'))
    # scan for @view_config and @subscriber decorators
    config.scan()
    # serve app
    app = config.make_wsgi_app()
    server = make_server('0.0.0.0', 8080, app)
    server.serve_forever()


TO:
def get_app():
    # configuration settings
    settings = {}
    settings['reload_all'] = True
    settings['debug_all'] = True
    settings['mako.directories'] = os.path.join(here, 'templates')
    settings['db'] = os.path.join(here, 'tasks.db')
    # session factory
    session_factory = UnencryptedCookieSessionFactoryConfig('itsaseekreet')
    # configuration setup
    config = Configurator(settings=settings, session_factory=session_factory)
    # routes setup
    config.add_route('list', '/')
    config.add_route('new', '/new')
    config.add_route('close', '/close/{id}')
    # static view setup
    config.add_static_view('static', os.path.join(here, 'static'))
    # scan for @view_config and @subscriber decorators
    config.scan()
    # serve app
    app = config.make_wsgi_app()
    return app

application = get_app()
if __name__ == '__main__':
    server = make_server('0.0.0.0', 8080, application)
    server.serve_forever()


[templates/layout.mako]

FROM:
  <link rel="shortcut icon" href="/static/favicon.ico">
  <link rel="stylesheet" href="/static/style.css">

TO:
  <link rel="shortcut icon" href="${request.application_url}/static/favicon.ico">
  <link rel="stylesheet" href="${request.application_url}/static/style.css">


I am using Debian 6, and the following is the relevant section in /etc/apache2/mods-enabled/wsgi.conf, where task.wsgi is a symbolic link to task.py in the same directory.

WSGIApplicationGroup %{GLOBAL}
WSGIPassAuthorization On

WSGIDaemonProcess pyramid user=david group=david threads=4 \
   python-path=/home/david/tasks/env/lib/python2.6/site-packages

WSGIScriptAlias /task /home/david/tasks/env/task/task.wsgi

<Directory /home/david/tasks/env>
  WSGIProcessGroup pyramid
  Order allow,deny
  Allow from all
</Directory>


[end]

2013-05-14

Set up Django 1.5.1 in DreamHost VPS

This post is about setting up Django 1.5.1 in a virtualenv in DreamHost VPS with Apache2 and mod_wsgi 3.4, and is mostly an update to the following post about three years ago:
http://www.wombatnation.com/2010/06/django-on-dreamhost-ps/comment-page-1
Software Versions:

   Apache 2.2.22
   Django 1.5.1
   mod_wsgi 3.4
   PIP 1.3.1
   Python 2.6.6
   VirtualEnv 1.9.1

1. Sign up a VPS account with DreamHost.

2. Create a regular user [user] in the VPS.  The Django project will run under this account.

3. Create an admin user [admin] in the VPS to manage the web server.  This admin account has sudo privileges.

4. Install Python PIP (1.3.1)

   [admin]$ sudo easy_install pip

   PIP is installed at /usr/local/bin/pip.

5. Upgrade VirtualEnv (from 1.4.9 to 1.9.1)

   [admin]$ sudo pip install --upgrade virtualenv

6. Create a virtualenv and install Django (1.5.1) in it

   [user]$ mkdir ~/django
   [user]$ virtualenv ~/django/env
   [user]$ source  ~/django/env/bin/activate
   [env]$ pip install django

7. Create a Django project (demo)

   [env]$ cd ~/django
   [env]$ django-admin.py startproject demo

8. Verify Django project is working

   [env]$ cd ~/django/demo
   [env]$ python manage.py runserver 0.0.0.0:8000

   Visit http://your.domain.com:8000 to verify Django project is up and running.

9. Install mod_wsgi 3.4

   [admin]$ mkdir ~/src
   [admin]$ cd ~/src
   [admin]$ wget http://modwsgi.google.com/files/mod_wsgi-3.4.tar.gz
   [admin]$ tar xzvf mod_wsgi-3.4.tar.gz
   [admin]$ cd mod_wsgi-3.4
   [admin]$ ./configure --with-apxs=/usr/local/dh/apache2/template/sbin/apxs --with-python=/usr/bin/python
   [admin]$ make
   [admin]$ sudo make install

   mod_wsgi is installed at /usr/local/dh/apach2/template/lib/modules/mod_wsi.so

10. Edit httpd.conf

   [admin]$ sudo vim /usr/local/dh/apache2/apache2-psNNNNNN/etc/httpd.conf

   psNNNNNN is your VPS id.

   add the following line in the LoadModule section:
   LoadModule wsgi_module /dh/apache2/template/lib/modules/mod_wsgi.so

   add the following line outside of VirtualHost directive:
   WSGIPythonPath /home/user/django/env/lib/python2.6/site-packages

   add following line inside the VirtualHost directive:
   WSGIScriptAlias /demo /home/user/django/demo/demo/wsgi.py

11. Restart Apache

   [admin]$ sudo /etc/init.d/httpd2 restart apache2-psNNNNNN

12. Edit wsgi.py

   [env]$ vim ~/django/demo/demo/wsgi.py

   add next two lines at the top of the file:
   import sys
   sys.path.insert(0, '/home/user/django/demo')

13. Create a Django app (demoapp) and update urls.py

   [env]$ cd ~/django/demo
   [env]$ python manage.py startapp demoapp
   [env]$ vim ~/django/demo/demoapp/views.py

   Make it look like:
   from django.http import HttpResponse

   def index(request):
       return HttpResponse("Hello world!")

   [env]$ vim ~/django/demo/demo/urls.py

   Add a url pattern:
   url(r'^$', 'demoapp.views.index')

14. Verify

   Visit http://your.domain.com/demo, will see "Hello World!" in browser.

-End-

2013-04-10

Password protect a PDF

The othe day, I wrote a CherryPy application that add a password to the PDF, and run it behind Apache2 with the mod_wsgi module.  The actual adding of password is done by pdftk console tool.

The entire CherryPy application is in one file main.py as below.

The index() method presents the UI to the web browser, asking the user to select the PDF and enter a password.

The upload() method shells out to the console and calls pdftk to add the password, and then returns the password-protected file back to the browser, letting the user to save the PDF to local storage (e.g. the user's hard disk).

The last part of the file deals with hooking it up with Apache2/mod_wsgi.  If main.py is run from the command line, then the CherryPy built-in HTTP server will start, otherwise, it assumes it is run behind Apache2 and a WSGI application is created.

# main.py
# requires cherrypy
# requires pdftk (command line tool)
# dkf 130401 creation

import os
import tempfile

import cherrypy

class PdfPass(object):
    def index(self):
        return """
        <html><body>
            <h2>Add password to PDF</h2>
            <form action="upload" method="post" enctype="multipart/form-data">
            <table>
            <tr>
            <td>Select PDF:</td>
            <td><input type="file" name="pdf" size="60"/></td>
            </tr>
            <tr>
            <td>Password:</td>
            <td><input type="password" name="pass1" value="" size="20" maxlength="40"/></td>
            </tr>
            <tr>
            <td>Password again:</td>
            <td><input type="password" name="pass2" value="" size="20" maxlength="40"/></td>
            </tr>
            <tr>
            <td colspan="2"><input type="submit" value="Add password"/></td>
            </tr>
            </table>
            </form>
        </body></html>
        """
    index.exposed = True

    def upload(self, pdf, pass1, pass2):
        if not pass1:
            return "Password cannot be emply!<br/>Please go back and correct."

        if pass1 != pass2:
            return "Passwords do not match!<br/>Please go back and correct."

        # read in the user uploaded pdf
        temp1 = tempfile.mktemp()
        with open(temp1, "wb") as f:
            f.write(pdf.file.read())
            f.close()

        # call pdftk to add password to pdf
        temp2 = tempfile.mktemp()
        os.system('pdftk %s output %s user_pw %s' % (temp1, temp2, pass1))
        with open(temp2, "rb") as f:
            data = f.read()
            f.close()

        # clean up temp files
        os.remove(temp1)
        os.remove(temp2)

        # deliver the password protected pdf to the user
        cherrypy.response.headers['Content-Type'] = "application/pdf"
        cherrypy.response.headers['Content-Disposition'] = 'attachment, filename="%s"' % pdf.filename
        return data
    upload.exposed = True

if __name__ == '__main__':
    cherrypy.quickstart(PdfPass())
else:
   # cherrypy.config.update({'environment': 'embedded'})
    application = cherrypy.Application(PdfPass(), script_name=None, config=None)


On the Apache side (Apache2 in Debian6), need to modify the /etc/apache2/sites-available/default, adding the following lines inside the <VirtualHost *.80> block.  Then the CherryPy application will be available at http://localhost/<url>.

# must run wsgi in daemon mode otherwise content-disposition may not work
WSGIDaemonProcess cherrypy processes=2 threads=15 display-name=%{GROUP}
WSGIProcessGroup cherrypy
WSGIScriptAlias /<url> /full/path/to/main.py
<Directory /full/path/to>
    Order allow,deny
    allow from all
</Directory>


-End-