Python 3.6 BaseHTTPRequestHandler not responding to requests - python

I have a simple python server script to conditionally respond to post requests. But is not sending responses
Have searched stack overflow and online to find examples of the same behaviour
from http.server import BaseHTTPRequestHandler
import json
verb_id = "http://id.tincanapi.com/verb/rated"
print('starting')
def respond(statement):
email = statement["actor"]["mbox"]
verb = statement["verb"]["id"]
response = statement["result"]["response"]
return ('Recieved statement from {email}, verb: {verb} with response: {response}'.format(email=email, verb=verb, response=response))
class RestHTTPRequestHandler(BaseHTTPRequestHandler):
def do_POST(self):
content_len = int(self.headers.get('Content-Length'))
post_body = self.rfile.read(content_len)
parsed_body = json.loads(post_body)
verb = parsed_body["statement"]["verb"]["id"]
if (verb == verb_id):
response = respond(parsed_body["statement"])
self.wfile.write(response.encode("utf-8"))
else:
print('recieved statement without rated')
self.wfile.write("Not processed".encode("utf-8"))
self.send_response(200)
self.end_headers()
return
httpd = HTTPServer(('0.0.0.0', 8025 ), RestHTTPRequestHandler)
while True:
httpd.handle_request()
The log shows
127.0.0.1 - - [11/Apr/2019 15:56:03] "POST / HTTP/1.1" 200 -
After each request, but Postman gives me 'Could not get any response'
I would expect to be getting a 200 to all post requests and the status returned from the if statement (either the result of the respond function or "Not Processed")
I am using Python 3.6
Thanks in advance

Related

Why does my python HttpServer dump all received requests on close?

I have the following request handler:
class MyHandler(BaseHTTPRequestHandler):
def do_POST(self) -> None:
if self.path == "/myRoute":
self.handle_route()
def handle_route(self):
print("Handling /myRoute")
received_bytes = self.rfile.read(int(self.headers['Content-Length']))
data = json.loads(received_bytes.decode('utf8').replace("'", '"'))
logger.info(json.dumps(data, indent=4, sort_keys=True))
self.send_response(200)
self.send_header("Content-Type", "application/json")
self.end_headers()
self.wfile.write(bytes("{\"Success\":true,\"Exception\":\"\"}", "utf-8"))
I am successfully handling POSTs to the route /myRoute. However, when I close after the following usage:
my_server = server.HTTPServer(("localhost", 8080), MyHandler)
my_thread = threading.Thread(target=my_server.serve_forever)
my_thread.daemon = True
my_thread.start()
# do some work
my_server.server_close()
I see a "dump" of all the POSTs received. This makes me think I am not properly flushing my response(s) back to the client:
127.0.0.1 - - [04/Aug/2020 12:01:58] "POST /myRoute HTTP/1.1" 200 -
127.0.0.1 - - [04/Aug/2020 12:02:35] "POST /myRoute HTTP/1.1" 200 -
127.0.0.1 - - [04/Aug/2020 12:03:11] "POST /myRoute HTTP/1.1" 200 -
.
.
.
What makes me think this even more is that it is the same number of requests I received during the execution of my application.
Am I properly sending my responses back to the client?
To fix this, I simply overrided the BaseHttpRequestHandler.log_message() to do nothing:
def log_message(self, format: str, *args: Any) -> None:
"""
Override of BaseHTTPRequestHandler to skip logging of messages.
Parameters
----------
format: str
The str format.
args: Any
The values to log.
"""
Then I only logged what I was interested in via the typical logger = logging.getLogger(__name__) in my route handling methods.

http post in ionic and python server

i use python json http server and need post json with ionic in this server,but http post method send options type.i need send post type.whats the problem?
Server:
def do_POST(self):
content_len = int(self.headers.getheader('content-length'))
post_body = self.rfile.read(content_len)
self.send_response(200)
self.end_headers()
data = json.loads(post_body)
self.wfile.write(data['id'])
return
ionic http post method:
$http.post(url, JSON.stringify({"id":"1"})).success(function (res) {
console.log("res" + res);
}).error(function (data, status, headers, config) {
console.log(data, status, headers,JSON.stringify (config));
});
Error from python server:
192.168.1.4 - - [10/Mar/2017 02:36:28] code 501, message Unsupported method ('OPTIONS')
192.168.1.4 - - [10/Mar/2017 02:36:28] "OPTIONS / HTTP/1.1" 501 -
This looks like a Cross-Origin Resource Sharing (CORS) preflight request issue.
I would recommend adding a do_OPTIONS method to your class:
def do_OPTIONS(self):
self.send_response(200, "ok")
self.send_header('Access-Control-Allow-Origin', '*')
self.send_header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS')
self.send_header("Access-Control-Allow-Headers", "X-Requested-With")
This will tell your browser that the POST request has Access-Control.
Another good SO article on this can be found here.

Python BaseHTTPRequestHandler: Respond with JSON

I have a Python class that inherits BaseHTTPRequestHandler and implements the method do_POST.
I currently only succeed to respond with an integer status, e.g. 200, using the following command at the end of the method:
self.send_response(200)
I am trying to also send some string as a part of the response. How should I do it?
At least in my environment (Python 3.7) i have to use
self.send_response(200)
self.send_header('Content-Type', 'application/json')
self.end_headers()
self.wfile.write(json_str.encode(encoding='utf_8'))
otherwise this error will be thrown:
TypeError: a bytes-like object is required, not 'str'
It turns out to be pretty simple, though there aren't many examples for it.
Just use:
self.wfile.write(YOUR_STRING_HERE)
Specifically for the case of json:
import json
json_string = json.dumps(YOUR_DATA_STRUCTURE_TO_CONVERT_TO_JSON)
self.wfile.write(json_string)
It's an old question. Still, if someone else might be wondering the same, here's my 2 cent.
If you are doing anything useful, apart from playing around with python, you should start looking for standard python frameworks to handle HTTP server operations, like Django or Flask.
That being said, there's a small stub that I use to act as a test server for my outgoing requests, which should answer your question. You can set any status code, header or response body by modifying it:
#!/usr/bin/env python
# Reflects the requests with dummy responses from HTTP methods GET, POST, PUT, and DELETE
# Written by Tushar Dwivedi (2017)
import json
from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler
from optparse import OptionParser
class RequestHandler(BaseHTTPRequestHandler):
def do_GET(self):
request_path = self.path
print("\n----- Request Start ----->\n")
print("request_path :", request_path)
print("self.headers :", self.headers)
print("<----- Request End -----\n")
self.send_response(200)
self.send_header("Set-Cookie", "foo=bar")
self.end_headers()
self.wfile.write(json.dumps({'hello': 'world', 'received': 'ok'}))
def do_POST(self):
request_path = self.path
# print("\n----- Request Start ----->\n")
print("request_path : %s", request_path)
request_headers = self.headers
content_length = request_headers.getheaders('content-length')
length = int(content_length[0]) if content_length else 0
# print("length :", length)
print("request_headers : %s" % request_headers)
print("content : %s" % self.rfile.read(length))
# print("<----- Request End -----\n")
self.send_response(200)
self.send_header("Set-Cookie", "foo=bar")
self.end_headers()
self.wfile.write(json.dumps({'hello': 'world', 'received': 'ok'}))
do_PUT = do_POST
do_DELETE = do_GET
def main():
port = 8082
print('Listening on localhost:%s' % port)
server = HTTPServer(('', port), RequestHandler)
server.serve_forever()
if __name__ == "__main__":
parser = OptionParser()
parser.usage = ("Creates an http-server that will echo out any GET or POST parameters, and respond with dummy data\n"
"Run:\n\n")
(options, args) = parser.parse_args()
main()
Again, even if you are just learning, and you even need to add 5-6 of if elses to the above to do what you are doing, it's better to do things right from the beginning, to avoid a lot of rework in future. Use a framework capable of handling boilerplate stuff for you.

Get file from POST request using Python's BaseHTTPServer

I'm running the code below to get POST messages. How do I get the file (sent with POST) and how do I respond with HTTP status code 200 OK?
#!/usr/bin/env python
import ssl
import BaseHTTPServer, SimpleHTTPServer
from BaseHTTPServer import BaseHTTPRequestHandler
class HttpHandler(BaseHTTPRequestHandler):
def do_POST(self):
print "got POST message"
# how do I get the file here?
print self.request.FILES
httpd = BaseHTTPServer.HTTPServer(('localhost', 4443), HttpHandler)
httpd.socket = ssl.wrap_socket(httpd.socket, certfile='./server.pem', server_side=True)
httpd.serve_forever()
$curl -X POST -d #../some.file https://localhost:4443/resource --insecure
AttributeError: 'SSLSocket' object has no attribute 'FILES'
BaseHTTPRequestHandler will process the first line and the headers of the http request then leave the rest up to you.
You need to read the remainder of the request using BaseHTTPRequestHandler.rfile
You can use self.send_response(200) to respond with 200 OK
Given the curl command you have specified the following should answer your question:
class HttpHandler(BaseHTTPRequestHandler):
def do_POST(self):
content_length = int(self.headers['Content-Length'])
file_content = self.rfile.read(content_length)
# Do what you wish with file_content
print file_content
# Respond with 200 OK
self.send_response(200)
Note that by using -d #../some.file in your curl command you are saying "this is an ascii file, oh and strip out the newlines and carriage returns please", thus there could be differences between the file you are using and the data you get in the request. Your curl command does not emulate an html form file-upload like post request.
It is generally available in
request.FILES
dictionary

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
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']
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)

Categories