Opened 10 years ago

Closed 10 years ago

#3003 closed enhancement (duplicate)

Support for AJP server in

Reported by: Henrik Vendelbo <info@…> Owned by: Adrian Holovaty
Component: Core (Management commands) Version: master
Severity: normal Keywords: ajp
Cc: Triage Stage: Unreviewed
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: UI/UX:


With the latest Apache HTTPd 2.2 the Ajp 1.3 protocol is supported as part of the new proxy support. This means that multiple backends can be balanced using standard Apache functionality. Flup already supports Ajp, so it should be a very minor task to add the support. I believe I have a working patch. The only thing I haven't looked at is automatic restarting of the Django process like the FastCGI implementation.


after def runfcgi(args):

def runajp(args):

"Runs this project as an AJP 1.3 application. Requires flup."
from django.conf import settings
from django.utils import translation
# Activate the current language, because it won't get activated later.


except AttributeError:


from django.core.servers.ajp import runajp

runajp.args = '[various KEY=val options, use runajp help for help]'

after 'runfcgi'

'runajp': runajp,

after 'runfcgi' elfi

elif action == 'runajp':

new django.core.servers.ajp

AJP 1.3 server that implements the WSGI protocol.

Uses the flup python package:

This is a adaptation of the flup package to add AJP 1.3 server support
to run Django apps from Web servers that support the AJP 1.3 protocol.
This module can be run standalone or from the django-admin /
scripts using the "runfcgi" directive.

Run with the extra option "help" for a list of additional options you can
pass to this server.

import sys, os

version = "0.1"
all = runajp?

AJP_HELP = r"""runajp:

Run this project as an ajp application. To do this, the
flup package from is

Usage: runajp --settings=yourproject.settings [ajp settings] runajp [ajp settings]

Optional Ajp13 settings: (setting=value)

host=HOSTNAME hostname to listen on..
port=PORTNUM port to listen on.
socket=FILE UNIX socket to listen on.
method=IMPL prefork or threaded (default prefork)
maxspare=NUMBER max number of spare processes to keep running.
minspare=NUMBER min number of spare processes to prefork.
maxchildren=NUMBER hard limit number of processes in prefork mode.
daemonize=BOOL whether to detach from terminal.
pidfile=FILE write the spawned process-id to this file.
workdir=DIRECTORY change to this directory when daemonizing


Run a "standard" ajp process on a file-descriptor
(for webservers which spawn your processes for you)

$ runajp method=threaded

Run an ajp server on a TCP host/port

$ runfcgi method=prefork host= port=8025

Run a ajp server on a UNIX domain socket (posix platforms only)

$ runfcgi method=prefork socket=/tmp/ajp.sock

Run a ajp as a daemon and write the spawned PID in a file

$ runfcgi socket=/tmp/ajp.sock method=prefork \

daemonize=true pidfile=/var/run/



'host': None,
'port': None,
'socket': None,
'method': 'fork',
'daemonize': None,
'workdir': '/',
'pidfile': None,
'maxspare': 5,
'minspare': 2,
'maxchildren': 50,


def ajp_help(message=None):

print AJP_HELP
if message:

print message

return False

def runajp(argset=[], kwargs):

options = AJP_OPTIONS.copy()
for x in argset:

if "=" in x:

k, v = x.split('=', 1)


k, v = x, True

options[k.lower()] = v

if "help" in options:

return ajp_help()


import flup

except ImportError, e:

print >> sys.stderr, "ERROR: %s" % e
print >> sys.stderr, " Unable to load the flup package. In order to run django"
print >> sys.stderr, " as a ajp application, you will need to get flup from"
print >> sys.stderr, " If you've already"
print >> sys.stderr, " installed flup, then make sure you have it in your PYTHONPATH."
return False

if optionsmethod? in ('prefork', 'fork'):

from flup.server.ajp_fork import WSGIServer
wsgi_opts = {

'maxSpare': int(optionsmaxspare?),
'minSpare': int(optionsminspare?),
'maxChildren': int(optionsmaxchildren?),


elif optionsmethod? in ('thread', 'threaded'):

from flup.server.ajp import WSGIServer
wsgi_opts = {}


return ajp_help("ERROR: Implementation must be one of prefork or thread.")

# Prep up and go
from django.core.handlers.wsgi import WSGIHandler

if optionshost? and optionsport? and not optionssocket?:

wsgi_optsbindAddress? = (optionshost?, int(optionsport?))

elif optionssocket? and not optionshost? and not optionsport?:

wsgi_optsbindAddress? = optionssocket?

elif not optionssocket? and not optionshost? and not optionsport?:

wsgi_optsbindAddress? = None


return ajp_help("Invalid combination of host, port, socket.")

if optionsdaemonize? is None:

# Default to daemonizing if we're running on a socket/named pipe.
daemonize = (wsgi_optsbindAddress? is not None)


if optionsdaemonize?.lower() in ('true', 'yes', 't'):

daemonize = True

elif optionsdaemonize?.lower() in ('false', 'no', 'f'):

daemonize = False


return ajp_help("ERROR: Invalid option for daemonize parameter.")

if daemonize:

from django.utils.daemonize import become_daemon

if optionspidfile?:

fp = open(optionspidfile?, "w")
fp.write("%d\n" % os.getpid())

WSGIServer(WSGIHandler(), wsgi_opts).run()

if name == 'main':


Change History (1)

comment:1 Changed 10 years ago by Simon G. <dev@…>

Has patch: set
Keywords: ajp added
Resolution: duplicate
Status: newclosed

#3047 does something similar and is more generic.

Note: See TracTickets for help on using tickets.
Back to Top