Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I debug POST requests with python's BaseHTTPServer / SimpleHTTPServer?

I found a script on this site for running a simple server via the command line with python.

I added some print lines in because I'd like to print out the GET and POST parameters via the command line for requests, but I can't seem to get them to show up anywhere.

If I just print our our the s variable (pprint (vars(s))) I end up seeing this:

{'client_address': ('127.0.0.1', 53373),
 'close_connection': 1,
 'command': 'GET',
 'connection': <socket._socketobject object at 0x10b6560c0>,
 'headers': <mimetools.Message instance at 0x10b689ab8>,
 'path': '/favicon.ico',
 'raw_requestline': 'GET /favicon.ico HTTP/1.1\r\n',
 'request': <socket._socketobject object at 0x10b6560c0>,
 'request_version': 'HTTP/1.1',
 'requestline': 'GET /favicon.ico HTTP/1.1',
 'rfile': <socket._fileobject object at 0x10b6538d0>,
 'server': <BaseHTTPServer.HTTPServer instance at 0x10b6893f8>,
 'wfile': <socket._fileobject object at 0x10b6536d0>}

I tried to then use the print command with each of the indices, (pprint (vars(s.connection))) but that's not working.

Here is the modified script:

#!/usr/bin/python
import time
import BaseHTTPServer
from pprint import pprint

HOST_NAME = 'localhost' # !!!REMEMBER TO CHANGE THIS!!!
PORT_NUMBER = 9000 # Maybe set this to 9000.


class MyHandler(BaseHTTPServer.BaseHTTPRequestHandler):
        def do_HEAD(s):
                s.send_response(200)
                s.send_header("Content-type", "text/html")
                s.end_headers()
        def do_GET(s):
                """Respond to a GET request."""
                s.send_response(200)
                s.send_header("Content-type", "text/html")
                s.end_headers()
                s.wfile.write("<html><head><title>Title goes here.</title></head>")
                s.wfile.write("<body><form action='.' method='POST'><input name='x' value='1' /><input type='submit' /></form><p>This is a test.</p>")
                # If someone went to "http://something.somewhere.net/foo/bar/",
                # then s.path equals "/foo/bar/".
                s.wfile.write("<p>GET: You accessed path: %s</p>" % s.path)
                s.wfile.write("</body></html>")
                pprint (vars(s))
        def do_POST(s):
                """Respond to a POST request."""
                s.send_response(200)
                s.send_header("Content-type", "text/html")
                s.end_headers()
                s.wfile.write("<html><head><title>Title goes here.</title></head>")
                s.wfile.write("<body><p>This is a test.</p>")
                s.wfile.write("<body><form action='.' method='POST'><input type='text' name='xxxxxxxxxxxx' value='0000000000000000000000' /><input type='submit' /></form><p>This is a test.</p>")
                # If someone went to "http://something.somewhere.net/foo/bar/",
                # then s.path equals "/foo/bar/".
                s.wfile.write("<p>POST: You accessed path: %s</p>" % s.path)
                s.wfile.write("</body></html>")
                pprint (vars(s))
                pprint (vars(s.connection))
                pprint (vars(s.headers))
                pprint (vars(s.request))
                pprint (vars(s.rfile))
                pprint (vars(s.server))
                pprint (vars(s.wfile))
                pprint (vars(s.fp))
                """pprint (vars(s.request))"""

if __name__ == '__main__':
        server_class = BaseHTTPServer.HTTPServer
        httpd = server_class((HOST_NAME, PORT_NUMBER), MyHandler)
        print time.asctime(), "Server Starts - %s:%s" % (HOST_NAME, PORT_NUMBER)
        try:
                httpd.serve_forever()
        except KeyboardInterrupt:
                pass
        httpd.server_close()
        print time.asctime(), "Server Stops - %s:%s" % (HOST_NAME, PORT_NUMBER)

How can I print out POST and GET parameters using a simple script?

Desired output via the command line would look something like:

1.0.0.127. - - [03/Oct/2012 16:02:05] "POST / HTTP/1.1" 200 -
foo=1
bar=2
bis=3
like image 956
cwd Avatar asked Oct 03 '12 20:10

cwd


People also ask

What does Python SimpleHTTPServer do?

The SimpleHTTPServer module is a Python module that enables a developer to lay the foundation for developing a web server. However, as sysadmins, we can use the module to serve files from a directory. The module loads and serves any files within the directory on port 8000 by default.

How do I stop SimpleHTTPServer?

CTRL+C is pressed to stop the server. Run the following command to start the webserver at 8080 port.


2 Answers

It's not tremendously obvious, but the handler is using sockets behind the scenes. So you need to read the raw data from the socket, and then interpret it.

Use the urlparse module.

  • In Python 2, you want urlparse.parse_qs.
  • In Python 3, the library is renamed: you want urllib.parse.parse_qs.

Import urlparse, and then modify your do_POST method like so:

def do_POST(s):
        """Respond to a POST request."""

        # Extract and print the contents of the POST
        length = int(s.headers['Content-Length'])
        post_data = urlparse.parse_qs(s.rfile.read(length).decode('utf-8'))
        for key, value in post_data.iteritems():
            print "%s=%s" % (key, value)

        s.send_response(200)
        s.send_header("Content-type", "text/html")
        s.end_headers()
        ...

Set up a simple test client:

#!/usr/bin/env python

import urllib
import urllib2

url = 'http://localhost:9000'
post_dict = {'foo' : 1,
             'bar' : 2,
             'bis' : 3}

params = urllib.urlencode(post_dict)
post_req = urllib2.Request(url)
post_req.add_data(params)

response = urllib2.urlopen(post_req)
response_data = response.read()
response.close()
print response_data

Start the server, and then run the client:

ire@localhost$ python http_server.py 
Wed Oct  3 21:38:51 2012 Server Starts - localhost:9000
foo=[u'1']
bar=[u'2']
bis=[u'3']
like image 90
ire_and_curses Avatar answered Sep 21 '22 15:09

ire_and_curses


You can use cgi module instead of urlparse. cgi implements POST params parsing out of the box. Using well-tested libraries seems better.

import cgi

def do_POST(self):
    form = cgi.FieldStorage(
        fp=self.rfile,
        headers=self.headers,
        environ={"REQUEST_METHOD": "POST"}
    )

    for item in form.list:
        print "%s=%s" % (item.name, item.value)
like image 29
Nowaker Avatar answered Sep 19 '22 15:09

Nowaker