pythonwsgi

WSGI Application - To Accept JSON Payload from and process and send email


I am trying to setup a App in python and wsgi to get some payload from cloud to on-prem and send email eventually. My Setup includes WSGI & NGINX but for testing purpose I am forming a payload locally using curl

My Code

I am using a code from existing stackoverflow and trying to modify it as per my need eventually. Currently facing problem with JSON data capture

from wsgiref.simple_server import make_server

import cgi
import subprocess
from email.message import EmailMessage
import json

def sendEmail(from_addr, to_addrs, msg_subject, msg_body):
    msg = EmailMessage()
    msg.set_content(msg_body)
    msg['From'] = from_addr
    msg['To'] = to_addrs
    msg['Subject'] = msg_subject
    sendmail_location = "/usr/sbin/sendmail"
    subprocess.run([sendmail_location, "-t", "-oi"], input=msg.as_bytes())

def is_post_request(environ):
    if environ['REQUEST_METHOD'].upper() != 'POST':
        return False
    content_type = environ.get('CONTENT_TYPE', 'application/x-www-form-urlencoded')
    print("I am printing here " + content_type)
    return (content_type.startswith('application/x-www-form-urlencoded' or content_type.startswith('multipart/form-data' or content_type.startswith('application/json'))))

def get_post_form(environ):
    print ("Printing environ" + str(environ))
    assert is_post_request(environ)
    input = environ['wsgi.input']
    post_form = environ.get('wsgi.post_form')
    if (post_form is not None
        and post_form[0] is input):
        return post_form[2]
    # This must be done to avoid a bug in cgi.FieldStorage
    environ.setdefault('QUERY_STRING', '')
    fs = cgi.FieldStorage(fp=input,
                          environ=environ,
                          keep_blank_values=1)
    new_input = InputProcessed()
    post_form = (new_input, input, fs)
    environ['wsgi.post_form'] = post_form
    environ['wsgi.input'] = new_input
    return fs

class InputProcessed(object):
    def read(self, *args):
        raise EOFError('The wsgi.input stream has already been consumed')
    readline = readlines = __iter__ = read


def application(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/html')])
    user = get_post_form(environ).getvalue('user')
    password = get_post_form(environ).getvalue('password')
    output = 'user is: '+user+' and password is: '+password
    return [output.encode()]

For now I am just trying to grab userName and Password from a local payload. I am sending the payload like below for testing purpose only

curl --header "Content-Type: application/json" --request POST --data '{"user":"abc","password":"xyz"}' http://localhost:8580

My final payload will have a {"method": "email"} which will call the sendmail function

My WSGI app is listening

ps -ef | grep wsgi
nginx     375580       1  0 09:16 ?        00:00:00 uwsgi --ini AutoApp.ini
nginx     375581  375580  0 09:16 ?        00:00:00 uwsgi --ini AutoApp.ini
nginx     375582  375580  0 09:16 ?        00:00:00 uwsgi --ini AutoApp.ini
nginx     375583  375580  0 09:16 ?        00:00:00 uwsgi --ini AutoApp.ini
nginx     375584  375580  0 09:16 ?        00:00:00 uwsgi --ini AutoApp.ini
nginx     375585  375580  0 09:16 ?        00:00:00 uwsgi --ini AutoApp.ini
root      379392  378312  0 10:00 pts/3    00:00:00 grep --color=auto wsgi

I am currently getting the below error:

$ curl --header "Content-Type: application/json" --request POST --data '{"user":"abc","password":"xyz"}' http://localhost:8580
Printing environ{'QUERY_STRING': '', 'REQUEST_METHOD': 'POST', 'CONTENT_TYPE': 'application/json', 'CONTENT_LENGTH': '31', 'REQUEST_URI': '/', 'PATH_INFO': '/', 'DOCUMENT_ROOT': '/usr/share/nginx/html', 'SERVER_PROTOCOL': 'HTTP/1.1', 'REQUEST_SCHEME': 'http', 'REMOTE_ADDR': '127.0.0.1', 'REMOTE_PORT': '43534', 'SERVER_PORT': '8580', 'SERVER_NAME': 'p0001.emea.net', 'HTTP_HOST': 'localhost:8580', 'HTTP_USER_AGENT': 'curl/7.61.1', 'HTTP_ACCEPT': '*/*', 'HTTP_CONTENT_TYPE': 'application/json', 'HTTP_CONTENT_LENGTH': '31', 'wsgi.input': <uwsgi._Input object at 0x7f34812cfb28>, 'wsgi.file_wrapper': <built-in function uwsgi_sendfile>, 'wsgi.version': (1, 0), 'wsgi.errors': <_io.TextIOWrapper name=2 mode='w' encoding='UTF-8'>, 'wsgi.run_once': False, 'wsgi.multithread': False, 'wsgi.multiprocess': True, 'wsgi.url_scheme': 'http', 'uwsgi.version': b'2.0.26', 'uwsgi.node': b'p0001.emea.net'}
I am printing here application/json
Traceback (most recent call last):
  File "./wsgi.py", line 61, in application
    user = get_post_form(environ).getvalue('user')
  File "./wsgi.py", line 36, in get_post_form
    assert is_post_request(environ)
AssertionError
[pid: 379691|app: 0|req: 1/1] 127.0.0.1 () {36 vars in 463 bytes} [Wed Jul 10 10:04:26 2024] POST / => generated 0 bytes in 1 msecs (HTTP/1.1 200) 1 headers in 44 bytes (0 switches on core 0)

Little guidance will be much appreciated here


Solution

  • Your last line of the function is_post_request() is wrong.
    Your code is:

    return (content_type.startswith('application/x-www-form-urlencoded' or content_type.startswith('multipart/form-data' or content_type.startswith('application/json'))))
    

    I think it should be:

    return (content_type.startswith('application/x-www-form-urlencoded') or content_type.startswith('multipart/form-data') or content_type.startswith('application/json'))
    

    or in a pretty look:

    return (  
        content_type.startswith('application/x-www-form-urlencoded') or 
        content_type.startswith('multipart/form-data') or 
        content_type.startswith('application/json')
    )