6

I`m trying to send simple html page with form data to user with GET, and then receive variables from form with POST. HTML file looks like:

<HTML>
<title> My Title</title>
<body>
<form  method="post" action="http.py">
<input name="Name" type="text"/>
<input name="Submit" type="submit" value="Submit" />
</form>
</body>
</HTML>

Here is python script:

import os
import cgi
import sys
from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler



class customHTTPServer(BaseHTTPRequestHandler):
        def do_GET(self):
                self.send_response(200)
                self.send_header('Content-type', 'text/html')
                self.end_headers()
                fh=open('index.html','r')
                self.wfile.write(fh.read())
                return

        def do_POST(self):
            form = cgi.FieldStorage()
            self.send_response(200)
            self.end_headers()
            self.wfile.write(form['Name'].value)


def main():
       try:
            server = HTTPServer(('',9111),customHTTPServer)
            print 'server started at port 8080'
            server.serve_forever()
       except KeyboardInterrupt:
            server.socket.close()

if __name__=='__main__':
       sys.exit(main())

But FieldStorage remains empty all the time. I already tried to check what is in self.rfile, and found that if i try to do self.rfile.readlines() , browser stuck and looks like script is waiting for the end of data stream. From where i should take Name variable that i`m submitting with POST?

Danylo Gurianov
  • 545
  • 2
  • 7
  • 21
  • Are you entering some value in your input field before submitting the form? Empty fields are not taken into account by `cgi.FieldStorage`, unless you pass `keep_blank_values=True` to its constructor. – Frédéric Hamidi Jul 04 '12 at 17:53
  • sure i did. Can`t figure out what i`m doing wrong .Also self.headers are fine, i can see Content-Type: application/x-www-form-urlencoded and other stuff there, but self.rfile is an issue. – Danylo Gurianov Jul 04 '12 at 17:56
  • 1
    Its not a cgi script. See my answer. – jdi Jul 04 '12 at 18:20

3 Answers3

13

In the original code, I just changed the line

form = cgi.FieldStorage()

to

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

and it seems to work as you intended.

Hat-tip to http://pymotw.com/2/BaseHTTPServer/ for the critical 'environ' setting. (By default, cgi.FieldStorage thinks it's dealing with a GET request.)

Michael Dyck
  • 2,153
  • 1
  • 14
  • 18
6

I think you might be mixing some concepts here. You have both the idea of a server and also a cgi script. No matter what your POST action is (http.py or whatever), your server is just going to take in a request. No CGI processing is actually happening. So you can first adjust your html template to this for simplicity:

<form  method="post" action="">

Then, you should reference this other question about how to read from the request, as opposed to trying to use the cgi fieldstorage:

import urlparse

...

    def do_POST(self):
        length = int(self.headers.getheader('content-length'))
        postvars = urlparse.parse_qs(self.rfile.read(length), keep_blank_values=1)
        self.send_response(200)
        self.end_headers()
        self.wfile.write(postvars)

The issue you were having, and because this is a very low level way to create a web server app, if you read indefinitely from the input stream, it will keep going and block. You check the header for the content length and only read that many bytes. You don't have any need for the cgi module at all, because this is not a cgi script.

A cgi script works by the server seeing the request is for a file of a matching type and location, and executing it in a subprocess like a normal program. It feeds the process the args and then gets back a response to ship back to the client. If this were a cgi script, the server would be in one module, running forever, and the cgi code would be in another with much simpler code to check the request.

Community
  • 1
  • 1
jdi
  • 90,542
  • 19
  • 167
  • 203
  • Many thanks for detailed explanation JDI. I`ve already found "how to read from request" topic by my own and tested approach - Yep , now it is clear. Indeed self.rfile is endless stream and that is why i could not get readline(s) from it. Now i understand idea behind it. I really need so , probably "low level " thing, cause i do not want to overload my script with some heavy imports. Thank you. – Danylo Gurianov Jul 04 '12 at 18:39
  • 1
    You say "You don't have any need for the cgi module at all, because this is not a cgi script", and yet the other question that you reference (for "how to read from the request") does indeed use the cgi module (specifically: cgi.parse_header, cgi.parse_multipart, and cgi.parse_qs), and it isn't a CGI script either. Clearly, the cgi module is useful for more than just CGI processing. Moreover, the source code for cgi.parse_multipart suggests that it should be subsumed by cgi.FieldStorage, so it seems like an approach involving cgi.FieldStorage should be workable, even concise. – Michael Dyck Aug 02 '14 at 05:04
  • 1
    This answer is not correct for the given question. cgi.FieldStorage properly handles both `application/x-www-form-urlencoded` and `multipart/form-data` (file uploads) content types. The given example, while useful to prevent blocking on the `rfile` filehandle, does not properly parse `multipart/form-data`. See Michael Dyck's suggestion for a correct answer to this question. – Ferry Boender Dec 31 '15 at 12:09
0

Here is URL for the approach to get POST vars in my case Here Detailed explanation is provided by JDI.

Community
  • 1
  • 1
Danylo Gurianov
  • 545
  • 2
  • 7
  • 21