apachepostwsgi

Apache: WSGI not routing


I am trying to use WSGI with a URL that I need to be able to POST to under Apache 2 (using flask). I am able to run my .wsgi independently of Apache (python cfg.wsgi) and it handles GET and POST requests correctly (tested with curl) but it does not operate when run under Apache: a curl GET request to the relevant URL (/watchdog.cfg) returns "404 Not Found" (and if I remove the WSGI mapping the GET works absolutely fine, fully within Apache, but then of course I don't get the POST facility I'm after)

Apache is loading the .wsgi file (its error log says so and a debug print at the top of the .wsgi file appears in a debug log), so Python paths and interpreters are likely correct; the issue, it seems, is that the .wsgi routing function is not being called by Apache (i.e. a debug print just inside the application function does not appear in the debug log when the URL in question (/watchdog.cfg) is requested).

My Apache .conf file is:

<VirtualHost *:80>
    DocumentRoot /home/http/
    WSGIScriptAlias /watchdog.cfg /home/http/cfg.wsgi
    <Directory /home/http>
        AllowOverride none
        Require all granted
    </Directory>
</VirtualHost>

apache2ctl -M | grep wsgi returns "wsgi_module (shared)", so I believe that Apache has the WSGI module installed. The file watchdog.cfg has permissions 0644, the file cfg.wsgi has permissions 0755.

My cfg.wsgi file is:

from flask import Flask, request, send_file
from os import path

FILE_NAME = 'watchdog.cfg'

application = Flask(__name__)

@application.route('/' + FILE_NAME, methods=['GET', 'POST'])
def cfg():
    file_path = '/home/http/' + FILE_NAME;
    if request.method == 'POST':
        jsonString = request.json
        with open(file_path, 'w') as f:
            f.write(jsonString)
        return "File updated successfully.", 200
    elif request.method == 'GET':
        if path.exists(file_path):
            return send_file(file_path, mimetype='application/json')
        else:
            return "File not found.", 404

if __name__ == '__main__':
    application.run(host='0.0.0.0', port=80)

This is on Raspbian Linux. Any suggestions as to why my routing function is not being called by Apache?


Solution

  • For the record, here is the Flask-less version of my cfg.wsgi which did work (same Apache .conf file contents):

    from os import path
    import logging
    
    FILE_NAME = 'watchdog.cfg'
    
    def application(environ, start_response):
        file_path = path.join(path.dirname(path.realpath(__file__)), FILE_NAME)
    
        try:
            if environ['REQUEST_METHOD'] == 'GET':
                if path.exists(file_path):
                    start_response('200 OK', [('Content-Type', 'application/json')])
                    with open(file_path, 'r') as f:
                        json_data = f.read()
                    return [str.encode(json_data)]
                else:
                    start_response('404 File Not Found', [('Content-Type', 'text/plain')])
                    return [b'File Not Found.']
    
            elif environ['REQUEST_METHOD'] == 'POST':
                try:
                    content_length = int(environ.get('CONTENT_LENGTH', 0))
                except ValueError:
                    start_response('400 Bad Request', [('Content-Type', 'text/plain')])
                    return [b'Invalid Content-Length']
    
                post_data = environ['wsgi.input'].read(content_length)
                try:
                    with open(file_path, 'w') as f:
                        f.write(post_data.decode('utf-8'))
                    start_response('200 OK', [('Content-Type', 'text/plain')])
                    return [b'File uploaded successfully!']
                except IOError as e:
                    start_response('500 Internal Server Error', [('Content-Type', 'text/plain')])
                    return [b'Failed to write file.']
    
            else:
                start_response('405 Method Not Allowed', [('Content-Type', 'text/plain')])
                return [b'Method Not Allowed.']
    
        except Exception as e:
            start_response('500 Internal Server Error', [('Content-Type', 'text/plain')])
            return [b'Internal Server Error.']