cn-py-serv/server/main.py

214 lines
6.5 KiB
Python

import threading
import socket
import logging
import os
import config
from .file_read import fileiter
from .response import Response
MAX_REQLINE = 64 * 1024
log = logging.getLogger(__name__)
class HTTPHandler:
def __init__(self, conn, addr):
self.conn = conn
self.addr = addr
self.read_fb = conn.makefile("rb")
self.write_fb = conn.makefile("wb")
self.http_type = ""
self.http_address = ""
self.http_get_request = {}
self.http_headers = {}
self.data = b""
self.pre_handle_connection()
def pre_handle_connection(self):
"""
Chain of handle first line in HTTP request
"""
raw = self.read_fb.readline(MAX_REQLINE + 1)
if len(raw) > MAX_REQLINE:
log.debug("Request line too long")
self.conn.send(b"")
self.conn.close()
return
req_line = raw.decode().rstrip("\r\n")
req_line_splited = req_line.split(" ")
if len(req_line_splited) != 3:
if len(req_line_splited) > 3:
log.debug("Request head too long")
else:
log.debug("Request head too small")
self.conn.send(b"")
self.conn.close()
return
if (req_line_splited[2] == "HTTP/1.0" or
req_line_splited[2] == "HTTP/1.1"):
self.http_type = req_line_splited[0]
else:
self.conn.send(b"")
self.conn.close()
return
if req_line_splited[0] == "GET" and req_line_splited[1].find("?") >= 0:
addr, get_req = req_line_splited[1].split("?", 1)
self.http_address = addr
for param in get_req.split("&"):
if len(param) == 0:
continue
elif param.find("=") >= 0:
name, val = param.split("=", 1)
else:
name, val = (param, "")
self.http_get_request.update({name: val})
else:
self.http_address = req_line_splited[1]
self.http_get_request = {}
self.http_header_handle()
def http_header_handle(self):
"""
Chain of handle headers in HTTP request
"""
while True:
raw = self.read_fb.readline(MAX_REQLINE + 1)
if len(raw) > MAX_REQLINE:
log.debug("Request header line too long")
self.conn.send(b"")
self.conn.close()
return
if raw == b"":
self.conn.send(b"")
self.conn.close()
return
if raw == b"\r\n":
self.http_data_handle()
break
else:
decoded_data = raw.decode("UTF-8").rstrip("\r\n")
decoded_data_split = decoded_data.split(":", 1)
self.http_headers.update(
{
decoded_data_split[0]: decoded_data_split[1].strip(" ")
})
def http_data_handle(self):
"""
Chain of receive data partitions HTTP request
"""
# TODO: Get from headers Content-Length and get body of request, else
# receive 1024 bytes
"""
raw = self.read_fb.readline(1024)
if len(raw) == 1024:
while True:
self.data += raw
if len(raw) < 1024:
break
else:
raw = self.read_fb.readline(1024)
"""
log.debug("Type of request: %s" % self.http_type)
log.debug("Requested address: %s" % self.http_address)
log.debug("Request headers: %s" % self.http_headers)
log.debug("Request in GET after ?: %s" % self.http_get_request)
log.debug("Data: %s" % self.data)
self.send_form_data()
def send_form_data(self):
"""
Chain of handle, pack & send data to client
"""
found = False
for elem in config.urls:
if self.http_address in elem.keys():
url_metadata = elem[self.http_address]
if self.http_type in url_metadata[0]:
found = True
if url_metadata[1] == "static-file":
r = Response(data=fileiter(url_metadata[2]))
for i in r:
self.write_fb.write(i)
self.write_fb.flush()
elif url_metadata[1] == "function":
func = url_metadata[2]
if func.__code__.co_argcount == 0:
func_ret = func()
elif func.__code__.co_argcount == 1:
func_ret = func(self.http_headers)
elif func.__code__.co_argcount == 2:
func_ret = func(self.http_headers, self.http_get_request)
for i in func_ret:
self.write_fb.write(i)
self.write_fb.flush()
else:
log.warning("Address configured on server, but type not allowed URL type in URLs list")
r = Response(status_code=404, data=b"Address configured on server, but type not allowed URL type in URLs list")
for i in r:
self.write_fb.write(i)
self.write_fb.flush()
if not found:
r = Response(status_code=404, data=b"Not found!")
for i in r:
self.write_fb.write(i)
self.write_fb.flush()
self.write_fb.close()
self.read_fb.close()
self.conn.close()
def start_http_server():
try:
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as main_server_socket:
main_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
main_server_socket.bind(("0.0.0.0", config.SETUP["server"]["port"]))
main_server_socket.listen()
while True:
conn, addr = main_server_socket.accept()
log.info("Accept connection from %s", addr[0])
thr_serv_conn = threading.Thread(target=HTTPHandler, args=(conn, addr,))
thr_serv_conn.run()
except KeyboardInterrupt:
log.info("Server get keyboard interrupt. Initialize server to stop")
log.info("==================== End of log ====================")