Created
July 5, 2016 11:57
-
-
Save mafayaz/faf938a896357c3a4c9d6da27edcff08 to your computer and use it in GitHub Desktop.
Writing Simple HTTP Server in Python (With REST and JSON)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
There are many already existing powerful http servers that can be used in python e.g. gevent, twisted web server. However, they are a bit complex to use and you cannot start them in a thread other than the main thread. | |
Here is a sample of basic http server using "BaseHTTPRequestHandler". The example exposed two rest interfaces: | |
To ingest records into the web server | |
To retrieve already ingested records from the web server | |
The records are assumed to be JSON based, however, any type of records can be ingested. | |
[sourcecode language="python" wraplines="false" collapse="false"] | |
from BaseHTTPServer import BaseHTTPRequestHandler,HTTPServer | |
from SocketServer import ThreadingMixIn | |
import threading | |
import argparse | |
import re | |
import cgi | |
class LocalData(object): | |
records = {} | |
class HTTPRequestHandler(BaseHTTPRequestHandler): | |
def do_POST(self): | |
if None != re.search('/api/v1/addrecord/*', self.path): | |
ctype, pdict = cgi.parse_header(self.headers.getheader('content-type')) | |
if ctype == 'application/json': | |
length = int(self.headers.getheader('content-length')) | |
data = cgi.parse_qs(self.rfile.read(length), keep_blank_values=1) | |
recordID = self.path.split('/')[-1] | |
LocalData.records[recordID] = data | |
print "record %s is added successfully" % recordID | |
else: | |
data = {} | |
self.send_response(200) | |
self.end_headers() | |
else: | |
self.send_response(403) | |
self.send_header('Content-Type', 'application/json') | |
self.end_headers() | |
return | |
def do_GET(self): | |
if None != re.search('/api/v1/getrecord/*', self.path): | |
recordID = self.path.split('/')[-1] | |
if LocalData.records.has_key(recordID): | |
self.send_response(200) | |
self.send_header('Content-Type', 'application/json') | |
self.end_headers() | |
self.wfile.write(LocalData.records[recordID]) | |
else: | |
self.send_response(400, 'Bad Request: record does not exist') | |
self.send_header('Content-Type', 'application/json') | |
self.end_headers() | |
else: | |
self.send_response(403) | |
self.send_header('Content-Type', 'application/json') | |
self.end_headers() | |
return | |
class ThreadedHTTPServer(ThreadingMixIn, HTTPServer): | |
allow_reuse_address = True | |
def shutdown(self): | |
self.socket.close() | |
HTTPServer.shutdown(self) | |
class SimpleHttpServer(): | |
def __init__(self, ip, port): | |
self.server = ThreadedHTTPServer((ip,port), HTTPRequestHandler) | |
def start(self): | |
self.server_thread = threading.Thread(target=self.server.serve_forever) | |
self.server_thread.daemon = True | |
self.server_thread.start() | |
def waitForThread(self): | |
self.server_thread.join() | |
def addRecord(self, recordID, jsonEncodedRecord): | |
LocalData.records[recordID] = jsonEncodedRecord | |
def stop(self): | |
self.server.shutdown() | |
self.waitForThread() | |
if __name__=='__main__': | |
parser = argparse.ArgumentParser(description='HTTP Server') | |
parser.add_argument('port', type=int, help='Listening port for HTTP Server') | |
parser.add_argument('ip', help='HTTP Server IP') | |
args = parser.parse_args() | |
server = SimpleHttpServer(args.ip, args.port) | |
print 'HTTP Server Running...........' | |
server.start() | |
server.waitForThread() | |
[/sourcecode] | |
Usage: | |
Copy paste the code above and name it simplewebserver.py | |
Starting WebServer: | |
python simplewebserver.py | |
POST addrecord example using curl: | |
curl -X POST http://localhost:8080/api/v1/addrecord/1 -d '{\"asif1\":\"test1\"}' -H "Content-Type: application/json" | |
GET record example using curl: | |
curl -X GET http://localhost:8080/api/v1/getrecord/test -H "Content-Type: application/json" |
Thanks for this code, helped alot in understanding json, but im getting the following error
Exception happened during processing of request from ('10.0.0.3', 49720)
Traceback (most recent call last):
File "/usr/lib/python3.5/socketserver.py", line 625, in process_request_thread
self.finish_request(request, client_address)
File "/usr/lib/python3.5/socketserver.py", line 354, in finish_request
self.RequestHandlerClass(request, client_address, self)
File "/usr/lib/python3.5/socketserver.py", line 681, in __init__
self.handle()
File "/usr/lib/python3.5/http/server.py", line 422, in handle
self.handle_one_request()
File "/usr/lib/python3.5/http/server.py", line 410, in handle_one_request
method()
File "pyjson.py", line 20, in do_POST
ctype, pdict = cgi.parse_header(self.headers.getheader('Content-Type'))
AttributeError: 'HTTPMessage' object has no attribute 'getheader'
@amirandap use .get() method
Thanks for this. A note for other readers: I believe it's python 2 only.
For example, this statement fails in python 3.7:
from BaseHTTPServer import BaseHTTPRequestHandler,HTTPServer
It needs to be
from http.server import BaseHTTPRequestHandler,HTTPServer
And so on.
Wrote a python3 version here: https://gist.github.com/dfrankow/f91aefd683ece8e696c26e183d696c29
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Cool 👍