I made a basic http server (I am developing my own because I want to do low level http analysis & manipulation in a later stage):
import socket
import threading
import queue
import time
class SocketServer:
"""
Basic Socket Server in python
"""
def __init__(self,host,port,max_theads):
self.host = host
self.port = port
self.server_socket = self.__initSocket()
self.max_theands = max_theads
self.request_queue = queue.Queue()
def __initSocket(self):
return socket.socket(socket.AF_INET, socket.SOCK_STREAM)
def __accept(self):
self.server_socket.listen(5)
while True:
client_socket, client_address = self.server_socket.accept()
self.request_queue.put((client_socket, client_address))
def __handle(self):
while True:
# Dequeue a request and process it
client_socket, address = self.request_queue.get()
# Read HTTP Request
# Log Http Request
# Manipulate Http Request
# Forward or respond
client_socket.sendall(b"""HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n<html><body>Hello World</body></html>\r\n""");
;
time.sleep(1)
client_socket.close()
self.request_queue.task_done()
def __initThreads(self):
for _ in range(self.max_theands):
threading.Thread(target=self.__handle, daemon=True).start()
def start(self):
self.server_socket.bind((self.host, self.port))
self.__initThreads()
self.__accept()
And I launch it into a seperate process:
#!/usr/bin/env python3
"""
1. Read settings
2. Bootstrap Manupilator
3. Bootstrap Control Panel
"""
import multiprocessing
from manipulator.http_socket_server import SocketServer
if __name__ == "__main__":
# @todo read settings file
host = "0.0.0.0"
port = 80
max_threads = 5
server = SocketServer(host, port, max_threads)
server_process = multiprocessing.Process(target=server.start)
server_process.start()
# Add other main application code here if needed
server_process.join()
But curl receives:
curl 10.0.0.2
<html><body>Hello World</body></html>
curl: (56) Recv failure: Connection reset by peer
despite the delay in __handle
:
time.sleep(1)
How I can close the socket gracefully?
One fix is to send the content length header:
content = '<html><body>Hello World</body></html>\r\n'.encode()
headers = f'HTTP/1.1 200 OK\r\nContent-Length: {len(content)}\r\nContent-Type: text/html\r\n\r\n'.encode()
client_socket.sendall(headers + content)
After change (and using port 8080):
C:\>curl localhost:8080
<html><body>Hello World</body></html>
Full working example:
import socket
import threading
import queue
class SocketServer:
def __init__(self, host, port, max_theads):
self.host = host
self.port = port
self.server_socket = self.__initSocket()
self.max_theands = max_theads
self.request_queue = queue.Queue()
def __initSocket(self):
return socket.socket(socket.AF_INET, socket.SOCK_STREAM)
def __accept(self):
self.server_socket.listen()
while True:
client_socket, client_address = self.server_socket.accept()
self.request_queue.put((client_socket, client_address))
def __handle(self):
while True:
client_socket, address = self.request_queue.get()
content = '<html><body>Hello World</body></html>\r\n'.encode()
headers = f'HTTP/1.1 200 OK\r\nContent-Length: {len(content)}\r\nContent-Type: text/html\r\n\r\n'.encode()
client_socket.sendall(headers + content)
client_socket.close()
self.request_queue.task_done()
def __initThreads(self):
for _ in range(self.max_theands):
threading.Thread(target=self.__handle, daemon=True).start()
def start(self):
self.server_socket.bind((self.host, self.port))
self.__initThreads()
self.__accept()
if __name__ == '__main__':
host = '0.0.0.0'
port = 8080
max_threads = 5
server = SocketServer(host, port, max_threads)
server.start()