This section describes how each request which comes to the application is handled step by step. It is important to be aware of all the phases.
So let us try to investigate what happens with each incoming HTTP request. Let us split its lifetime into the following stages:
At this point the request is processed by Django. It is responsibility is to handle the incoming request object and return an appropriate response. The class, which is responsible for handling the request, is called django.core.handlers.core.WSGIHandler. The first step, which was actually triggered by AppEngine in order to conform with WSGI pattern, is to invoke the object's call method. We will not go into details of what exactly happens then and how. Suffice it to say that WSGIRequest object is created and passed to get_response method.
One of the first steps performed by get_response method is to apply the registered middleware classes. Those classes are responsible taking certain actions before the request can be dispatched. The middleware classes specified for Melange project are listed in settings.py module under MIDDLEWARE_CLASSES constant.
At the time of this writing three classes are registered for the project.
The main purpose is to record AppEngine statistics. It is provided by AppEngine specifically to be installed as one of Django middlewere classes. They recommend it should be listed at the first position, as other middleware classes may make RPC calls.
The purpose of this class is to handle POST requests containing file uploads. The way AppEngine works is that they first have to be uploaded to Blobstore.
It checks the presence of XSRF token for POST requests.
Writing your own middleware class, one has to conform to the interface which is specified by Django and can be found here. In particular, in order for a task to be processed in a request middleware class, i.e. before the request is actually dispatched to one of application specific handlers, process_request has to be implemented.
The next step which is performed by Django in get_response function is to resolve the user specified URL and find a callback object. Here is where Melange part of the processing really begins. Below it will be described what callback object is and how it produces response.
There is base RequestHandler class which outlines behavior and determines workflow. The first method which is invoked is call method, as get_response function treats these objects as callbacks. To summarize the methods does the following:
- It is checked if the application is currently in maintenance mode, if so MaintainceMode exception is raised and users are asked to try again later.
- Access check is called so as to determine if the current user is allowed to submit the request
- The request is delegated to an appropriate function for processing
- In case of exceptions, they are caught and handled
- The response is returned to Django layer
Right now we will describe each step individually. Let us start with the first one. At the beginning of each request processing, there is initialization phase which is performed by by init() function. It it responsible for returning three values: a RequestData object associated with the request, an instance of AccessChecker and Mutator.
The second step is to check if the application is currently in the maintenance mode. This may happen if the option has been enabled by developers or if some App Engine features are limited or have been purposely turned off. The latter may happen, for example, if datastore writes are disabled or if there is scheduled maintenance undergoing.
If the system is running normally, the next phase is to check if the current user is allowed to submit the request. In order to do that a concrete class which inherits from RequestHandler must implement checkAccess method. This method should raise AccessViolation, if the user is not permitted to access the requested resource for some reason. The logic ought to be delegated to a utility function in AccessChecker class.
It is important to mention that for users with developer status a deferent implementation of AccessChecker is used. They are allowed to access every single view, so shortly speaking, the actual access checking functions are bypassed. Therefore no state of objects should be changed by them. In particular, AccessChecker methods should not extend RequestData object with any extra fields, as they may not be defined for developers. It may introduce some vicious errors which will encountered only by that one specific type of users.
Access checking is followed by dispatching the actual request processing to a function designated for that based on the request's type.