Skip to content

Instantly share code, notes, and snippets.

@mafayaz
Created July 5, 2016 11:57
Show Gist options
  • Star 13 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save mafayaz/faf938a896357c3a4c9d6da27edcff08 to your computer and use it in GitHub Desktop.
Save mafayaz/faf938a896357c3a4c9d6da27edcff08 to your computer and use it in GitHub Desktop.
Writing Simple HTTP Server in Python (With REST and JSON)
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"
@gangadhars
Copy link

Cool 👍

@amirandap
Copy link

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'

@Edorka
Copy link

Edorka commented Feb 18, 2018

@amirandap use .get() method

@dfrankow
Copy link

dfrankow commented Oct 12, 2019

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.

@dfrankow
Copy link

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment