Advertisement
Snusmumriken

py_simple_async_http_server

Jul 5th, 2020 (edited)
291
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 5.97 KB | None | 0 0
  1. import select
  2. from multiprocessing import Process, Array, Value
  3. import sys
  4. import time
  5.  
  6. def thread(request, response, status, type):
  7.     def thread_return(_message, _status=200, _type=b"text/html"):
  8.         response.value = _message
  9.         type.value     = _type
  10.         status.value   = _status
  11.    
  12.         thread_return(b"Hello world")
  13.     return sys.exit(0)
  14.  
  15. class Server():
  16.     server = None
  17.    
  18.     readable  = [] # request reading
  19.     writeable = [] # response writeing
  20.     clients   = {} # in process
  21.    
  22.     def __init__(self, host, port):
  23.         import socket
  24.         print("Create HTTP server " + host + ":" + str(port))
  25.         self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  26.         self.server.bind((host, port))
  27.         self.server.listen(10)
  28.         self.server.setblocking(0)
  29.         #self.server.settimeout(0)
  30.    
  31.     def add(self, sock, addr):
  32.         self.readable.append(sock)
  33.        
  34.         data = {
  35.             "sock":      sock,
  36.             "addr":      addr,
  37.             "raw_input": b"",
  38.             "data":      b"",
  39.             "response":  Array("c", 1024*10 * 10),
  40.             "status":    Value("d", -1),
  41.             "type":      Array("c", 512),
  42.             "timeout":   time.time() + 3
  43.         }
  44.        
  45.         data["type"].value = b"text/html"
  46.         self.clients[sock] = data
  47.        
  48.     def remove(self, sock):
  49.         addr = self.get(sock)["addr"]
  50.        
  51.         if sock in self.readable:
  52.             self.readable.remove(sock)
  53.         if sock in self.writeable:
  54.             self.writeable.remove(sock)
  55.         if sock in self.clients:
  56.             del self.clients[sock]
  57.             print(addr + " del")
  58.         sock.close()
  59.         print("Clients after remove: %d, r/w: %d %d" % (len(self.clients), len(self.readable), len(self.writeable)))
  60.    
  61.  
  62.     def parseHeaders(self, cli, data):
  63.         print("Start parse headers: " + data)
  64.         data = data.splitlines()
  65.         firstline, headers = data[0], data[1:]
  66.        
  67.         headers = dict([i.split(": ") for i in headers[1:]])
  68.        
  69.         if "Content-Length" in headers:
  70.             headers["Content-Length"] = int(headers["Content-Length"])
  71.        
  72.         firstline = firstline.split(" ")
  73.        
  74.         method, address, version = firstline[0], firstline[1], firstline[2]
  75.        
  76.         query  = ""
  77.         dquery = {}
  78.        
  79.         qindex = address.find("?")
  80.         if qindex > 0:
  81.             address, query = address[:qindex], address[qindex+1:]
  82.             for qc in query.split("&"):
  83.                 ql = qc.split("=")
  84.                 dquery[ql[0]] = (len(ql) == 2) and ql[1] or True
  85.    
  86.         request = {}
  87.         request["headers"] = headers
  88.         request["query"],  request["dquery"] = query, dquery
  89.         request["method"], request["address"], request["proto"] = method, address, version
  90.         print("Finish parse headers " + str(request))
  91.         return request
  92.  
  93.     def sendResponse(self, cli):
  94.         message = cli["response"].value
  95.         status  = cli["status"].value
  96.         type    = cli["type"].value
  97.         print("Send response: " + str(len(message)) + " " + str(message)[:50])
  98.         response = b"HTTP/1.0 %d\r\nContent-Type:%s\r\nContent-Length:%d\r\n\r\n%s" % (status, type, len(message), message)
  99.         cli["sock"].send(response)
  100.         cli["sock"].shutdown(1)
  101.    
  102.     def get(self, sock):
  103.         return self.clients[sock]
  104.        
  105.     def append(self, cli, data):
  106.         cli["timeout"] = time.time() + 60
  107.         print("Append " + cli["addr"] + " " + str(cli["raw_input"]) + "\n" + str(data) + "\n\n")
  108.        
  109.         cli["raw_input"] += data
  110.         raw_input = cli["raw_input"]
  111.        
  112.         if not "request" in cli:
  113.             index = raw_input.find(b"\r\n\r\n")
  114.             if index > 0:
  115.                 headers = raw_input[:index]
  116.                 data = raw_input[index + 4:]
  117.                 print("Headers: " + str(headers))
  118.                 print("Data: " + str(data))
  119.                
  120.                 request = self.parseHeaders(cli, headers.decode())
  121.                 cli["request"] = request
  122.                
  123.                 if "Content-Length" in request["headers"]:
  124.                     cli["length"]  = request["headers"]["Content-Length"]
  125.                     cli["data"]    = data
  126.                 else:
  127.                     return True
  128.                    
  129.         elif "length" in cli and len(cli["data"]) < cli["length"]:
  130.             cli["data"] += data
  131.             if len(cli["data"]) == cli["length"]:
  132.                 return True
  133.    
  134.     def process_sock(self, cli):
  135.         print("\r\n\r\nCREATE THREAD " + str(cli["addr"]) + "  " + str(cli["request"]))
  136.         prep = getattr(self, "preprocess", None)
  137.         if callable(prep):
  138.             prep(cli)
  139.  
  140.         p = Process(target=thread, args=(cli["request"], cli["response"], cli["status"], cli["type"]))
  141.         p.start()
  142.         cli["p"] = p
  143.        
  144.     def update(self):
  145.         if not self.server:
  146.             print("Server: update closed server")
  147.             return
  148.        
  149.         readable, writeable, exceptional = select.select([self.server], [], [], 0)
  150.         if len(readable) > 0:
  151.             s_sock, s_addr = self.server.accept()
  152.             if s_sock:
  153.                 s_addr = str(s_addr[0]) + ":" + str(s_addr[1])
  154.                 print("Add client " + s_addr)
  155.                 self.add(s_sock, s_addr)
  156.                
  157.         print("Clients: c: %d, r/w: %d %d   %f" % (len(self.clients), len(self.readable), len(self.writeable), time.time()))
  158.         if len(self.readable) != 0 or len(self.writeable) != 0:
  159.             readable, writeable, exceptional = select.select(self.readable, self.writeable, [], 0)
  160.            
  161.             for sock in readable:
  162.                 cli  = self.get(sock)
  163.                 data = sock.recv(128)
  164.                 if data and self.append(cli, data):
  165.                     self.readable.remove(sock)
  166.                     cli["request"]["data"] = cli["data"]
  167.                     self.process_sock(cli)
  168.                
  169.             for sock in writeable:
  170.                 print("Remove client " + self.get(sock)["addr"])
  171.                 postp = getattr(self, "postprocess", None)
  172.                 if callable(postp):
  173.                     postp(cli)
  174.                 self.remove(sock)
  175.  
  176.         to_remove = []
  177.         for sock in self.clients:
  178.             cli = self.clients[sock]
  179.             if cli["status"].value != -1.0:
  180.                 print("Send " + cli["addr"] + " " + str(cli["response"].value))
  181.                 self.sendResponse(cli)
  182.                 self.writeable.append(cli["sock"])
  183.             if time.time() > cli["timeout"]:
  184.                 to_remove.append(sock)
  185.        
  186.         for sock in to_remove:
  187.             self.remove(sock)
  188.    
  189.     def close(self):
  190.         if self.server:
  191.             for sock in self.clients:
  192.                 sock = self.clients[sock]["sock"].close()
  193.             self.server.close()
  194.             self.readable.clear()
  195.             self.writeable.clear()
  196.             self.clients.clear()
  197.         self.server = None
  198.  
  199.  
  200. if __name__ == '__main__':
  201.     host, port = "", 9265
  202.     print("Create HTTP server " + host + ":" + str(port))
  203.     server = Server(host, port)
  204.    
  205.     try:
  206.         while True:
  207.             server.update()
  208.    
  209.     except KeyboardInterrupt:
  210.         server.close()
  211.         sys.exit(0)
  212.        
  213.     server.close()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement