Last active
April 8, 2024 13:13
-
-
Save 0x48piraj/5f5b865fccb220aae3ee8660b74101b5 to your computer and use it in GitHub Desktop.
Basic key-value store over HTTP using sockets in Python3
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
import sys | |
import json | |
import os | |
from socket import ( | |
socket, | |
AF_INET, | |
SOCK_STREAM, | |
SO_REUSEADDR, | |
SOL_SOCKET | |
) | |
class InvalidRequest(Exception): | |
pass | |
class RequestParser(object): | |
"Class for parsing request headers in a manageable fashion" | |
def __init__(self, raw_request): | |
self._raw_request = raw_request | |
self._method, self._path, self._protocol, self._headers = self.parse() | |
def parse(self): | |
temp = [i.strip() for i in self._raw_request.splitlines()] | |
# figuring out request method, path, HTTP protocol | |
method, path, protocol = [i.strip() for i in temp[0].split()] | |
# construct headers in case of a GET request | |
headers = {} | |
if 'GET' == method: | |
for k, v in [i.split(':', 1) for i in temp[1:-1]]: | |
headers[k.strip()] = v.strip() | |
else: | |
raise InvalidRequest('Only accepts GET requests') | |
return method, path, protocol, headers | |
def __repr__(self): | |
return repr({'method': self._method, 'path': self._path, 'protocol': self._protocol, 'headers': self._headers}) | |
HOST, PORT, CLRF = "127.0.0.1", 8080, "\r\n" | |
if os.path.isfile('keystore.txt'): | |
f = open('keystore.json', 'r') | |
keystore = json.load(f) | |
else: | |
keystore = {} | |
with socket(AF_INET, SOCK_STREAM) as sock: | |
sock.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) | |
sock.bind((HOST, PORT)) | |
sock.listen(1) | |
while True: | |
try: | |
conn, addr = sock.accept() | |
req = conn.recv(1024).decode() | |
if not req: | |
break | |
request = RequestParser(req) | |
print(req) | |
print("=" * 20) | |
if '/set?' in request._path: | |
k, v = request._path.split('?')[-1].split('=') | |
keystore[k] = v | |
conn.send(f'HTTP/1.1 200 OK{CLRF}'.encode()) | |
conn.send(f'Content-Type: text/html{CLRF*2}'.encode()) | |
conn.send(f'<h1>Stored key: {k}</h1>'.encode()) | |
with open('keystore.json', 'w', encoding='utf-8') as file: | |
json.dump(keystore, file, indent=4) | |
elif '/get?' in request._path: | |
k = request._path.split('?')[-1].split('=')[-1] | |
print("Stored value:", keystore[k]) | |
conn.send(f'HTTP/1.1 200 OK{CLRF}'.encode()) | |
conn.send(f'Content-Type: text/html{CLRF*2}'.encode()) | |
conn.send(f'<h1>Stored Value: {keystore[k]}</h1>'.encode()) | |
else: | |
conn.send(f'HTTP/1.1 200 OK{CLRF}'.encode()) | |
conn.send(f'Content-Type: text/html{CLRF*2}'.encode()) | |
conn.send('Hello world.'.encode()) | |
conn.close() | |
except KeyboardInterrupt: | |
sys.exit() | |
except InvalidRequest as e: | |
conn.send(f'HTTP/1.1 400 Bad Request{CLRF}'.encode()) | |
conn.send(f'Content-Type: text/html{CLRF*2}'.encode()) | |
conn.send(f'Invalid Request: {e}'.encode()) | |
except Exception as e: | |
print(e) | |
sock.shutdown | |
sock.close() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment