Code review
Change name from start_http_server() to init_server_socket(). Improvements: - Change child threads to daemon type; - Send client HTTP error codes & add new HTTP codes description; - Change log output format; - Add some debug log strings; Remove: - Startup ASCII logo;
This commit is contained in:
parent
9155cb17ed
commit
c9154cdb08
5 changed files with 84 additions and 68 deletions
|
@ -20,10 +20,10 @@ SETUP = {
|
||||||
if SETUP["setup"]["log_to_file"]:
|
if SETUP["setup"]["log_to_file"]:
|
||||||
logging.basicConfig(filename=os.path.join(os.getcwd(), "log", "main.log"), filemode="a", encoding="UTF-8",
|
logging.basicConfig(filename=os.path.join(os.getcwd(), "log", "main.log"), filemode="a", encoding="UTF-8",
|
||||||
level=SETUP["setup"]["log_level"],
|
level=SETUP["setup"]["log_level"],
|
||||||
format="[%(name)s][%(asctime)s][%(levelname)s] %(message)s")
|
format="[%(asctime)s][%(levelname)s][%(name)s] %(message)s")
|
||||||
else:
|
else:
|
||||||
logging.basicConfig(encoding="UTF-8", level=SETUP["setup"]["log_level"],
|
logging.basicConfig(encoding="UTF-8", level=SETUP["setup"]["log_level"],
|
||||||
format="[%(name)s][%(asctime)s][%(levelname)s] %(message)s")
|
format="[%(asctime)s][%(levelname)s][%(name)s] %(message)s")
|
||||||
|
|
||||||
|
|
||||||
urls = [
|
urls = [
|
||||||
|
|
22
main.py
22
main.py
|
@ -2,7 +2,7 @@
|
||||||
import os
|
import os
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from server.main import start_http_server
|
from server.main import init_server_socket
|
||||||
import config
|
import config
|
||||||
|
|
||||||
|
|
||||||
|
@ -12,20 +12,10 @@ if __name__ == "__main__":
|
||||||
try:
|
try:
|
||||||
os.mkdir(os.path.join(os.getcwd(), "log"))
|
os.mkdir(os.path.join(os.getcwd(), "log"))
|
||||||
except OSError:
|
except OSError:
|
||||||
pass #print("dir is exists")
|
log.debug("Directory \"log\" is exists")
|
||||||
|
else:
|
||||||
|
log.debug("Directory \"log\" created")
|
||||||
|
|
||||||
project_logotype = """
|
log.error("Attempt to start server...")
|
||||||
________ _ _ _______ _______ ______ _ _
|
|
||||||
| ______ | | |_ | | | _____ | | _____| | ___ | | | | |
|
|
||||||
| | |_| | | | | | | |_| | | | | | | |_| |_|
|
|
||||||
| | | ||_| | | |_|___ | |___ | |___| | | | | |
|
|
||||||
| | | | |_| | | |___|_ | ___| | ____| |_| |_|
|
|
||||||
| | _ | | |_|| | _ | | | | | |_|_ | | | |
|
|
||||||
| |______| | | | |_ | | |_____| | | |_____ | | |_|_ |_|_|_|
|
|
||||||
|________| |_| |_| |_______| |_______| | | |_| |_|
|
|
||||||
|
|
||||||
============================== Start HTTP server ==============================="""
|
init_server_socket()
|
||||||
|
|
||||||
log.info(project_logotype)
|
|
||||||
|
|
||||||
start_http_server()
|
|
||||||
|
|
0
server/__init__.py
Normal file
0
server/__init__.py
Normal file
120
server/main.py
120
server/main.py
|
@ -20,8 +20,8 @@ class HTTPHandler:
|
||||||
self.conn = conn
|
self.conn = conn
|
||||||
self.addr = addr
|
self.addr = addr
|
||||||
|
|
||||||
self.read_fb = conn.makefile("rb")
|
self.read_fd = conn.makefile("rb")
|
||||||
self.write_fb = conn.makefile("wb")
|
self.write_fd = conn.makefile("wb")
|
||||||
|
|
||||||
self.http_method = ""
|
self.http_method = ""
|
||||||
self.http_address = ""
|
self.http_address = ""
|
||||||
|
@ -30,22 +30,38 @@ class HTTPHandler:
|
||||||
|
|
||||||
self.data = b""
|
self.data = b""
|
||||||
|
|
||||||
self.pre_handle_connection()
|
self.__handle_connection()
|
||||||
|
|
||||||
def pre_handle_connection(self):
|
def __write_data(self, data_iter):
|
||||||
|
for chunk in data_iter:
|
||||||
|
self.write_fd.write(chunk)
|
||||||
|
self.write_fd.flush()
|
||||||
|
|
||||||
|
def __read_data(self, size=MAX_REQLINE+1):
|
||||||
|
return self.read_fd.readline(size)
|
||||||
|
|
||||||
|
def __close(self):
|
||||||
|
self.write_fd.close()
|
||||||
|
self.read_fd.close()
|
||||||
|
self.conn.close()
|
||||||
|
|
||||||
|
def __handle_connection(self):
|
||||||
"""
|
"""
|
||||||
Chain of handle first line of HTTP request
|
Chain of handle first line of HTTP request
|
||||||
"""
|
"""
|
||||||
raw = self.read_fb.readline(MAX_REQLINE + 1)
|
raw = self.__read_data()
|
||||||
|
|
||||||
if len(raw) > MAX_REQLINE:
|
if len(raw) > MAX_REQLINE:
|
||||||
log.debug("Request line too long")
|
log.debug("Request line too long")
|
||||||
self.conn.send(b"")
|
|
||||||
self.conn.close()
|
r = Response(status_code=413, data=b"Request line too long")
|
||||||
|
self.__write_data(r)
|
||||||
|
|
||||||
|
self.__close()
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# FIXME data maybe isn't UTF-8 or just binary
|
||||||
req_line = raw.decode().rstrip("\r\n")
|
req_line = raw.decode().rstrip("\r\n")
|
||||||
# method, address, protocol = req_line_splited
|
|
||||||
req_line_splited = req_line.split(" ")
|
req_line_splited = req_line.split(" ")
|
||||||
|
|
||||||
if len(req_line_splited) != 3:
|
if len(req_line_splited) != 3:
|
||||||
|
@ -54,18 +70,29 @@ class HTTPHandler:
|
||||||
else:
|
else:
|
||||||
log.debug("Request head too small")
|
log.debug("Request head too small")
|
||||||
|
|
||||||
self.conn.send(b"")
|
r = Response(status_code=400, data=b"Bad request")
|
||||||
self.conn.close()
|
self.__write_data(r)
|
||||||
|
self.__close()
|
||||||
return
|
return
|
||||||
|
|
||||||
if (req_line_splited[2] == "HTTP/1.0" or
|
|
||||||
req_line_splited[2] == "HTTP/1.1"):
|
|
||||||
|
|
||||||
http_method = req_line_splited[0]
|
if req_line_splited[2].startswith("HTTP/"):
|
||||||
|
if (req_line_splited[2] == "HTTP/1.0" or
|
||||||
|
req_line_splited[2] == "HTTP/1.1"):
|
||||||
|
http_method = req_line_splited[0]
|
||||||
|
else:
|
||||||
|
log.debug("Unsupported HTTP version")
|
||||||
|
|
||||||
|
r = Response(status_code=505, data=b"HTTP Version Not Supported")
|
||||||
|
self.__write_data(r)
|
||||||
|
self.__close()
|
||||||
|
return
|
||||||
else:
|
else:
|
||||||
self.conn.send(b"")
|
log.debug("Protocol isn't HTTP version 1.0 or 1.1")
|
||||||
self.conn.close()
|
|
||||||
|
r = Response(status_code=400, data=b"Bad request")
|
||||||
|
self.__write_data(r)
|
||||||
|
self.__close()
|
||||||
return
|
return
|
||||||
|
|
||||||
if http_method == "GET":
|
if http_method == "GET":
|
||||||
|
@ -85,8 +112,11 @@ class HTTPHandler:
|
||||||
elif http_method == "PATCH":
|
elif http_method == "PATCH":
|
||||||
self.http_method = HTTPMethod.PATCH
|
self.http_method = HTTPMethod.PATCH
|
||||||
else:
|
else:
|
||||||
self.conn.send(b"")
|
log.debug("Method not allowed")
|
||||||
self.conn.close()
|
|
||||||
|
r = Response(status_code=405, data=b"Method not allowed")
|
||||||
|
self.__write_data(r)
|
||||||
|
self.__close()
|
||||||
return
|
return
|
||||||
|
|
||||||
if (self.http_method == HTTPMethod.GET and
|
if (self.http_method == HTTPMethod.GET and
|
||||||
|
@ -110,14 +140,14 @@ class HTTPHandler:
|
||||||
self.http_address = req_line_splited[1]
|
self.http_address = req_line_splited[1]
|
||||||
self.http_get_request = {}
|
self.http_get_request = {}
|
||||||
|
|
||||||
self.http_header_handle()
|
self.__http_header_handle()
|
||||||
|
|
||||||
def http_header_handle(self):
|
def __http_header_handle(self):
|
||||||
"""
|
"""
|
||||||
Chain of handle headers in HTTP request
|
Chain of handle headers in HTTP request
|
||||||
"""
|
"""
|
||||||
while True:
|
while True:
|
||||||
raw = self.read_fb.readline(MAX_REQLINE + 1)
|
raw = self.__read_data()
|
||||||
|
|
||||||
if len(raw) > MAX_REQLINE:
|
if len(raw) > MAX_REQLINE:
|
||||||
log.debug("Request header line too long")
|
log.debug("Request header line too long")
|
||||||
|
@ -126,12 +156,13 @@ class HTTPHandler:
|
||||||
return
|
return
|
||||||
|
|
||||||
if raw == b"":
|
if raw == b"":
|
||||||
|
log.debug("Client not send data, close connection")
|
||||||
self.conn.send(b"")
|
self.conn.send(b"")
|
||||||
self.conn.close()
|
self.conn.close()
|
||||||
return
|
return
|
||||||
|
|
||||||
if raw == b"\r\n":
|
if raw == b"\r\n" or raw == b"\n":
|
||||||
self.http_data_handle()
|
self.__http_data_handle()
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
decoded_data = raw.decode("UTF-8").rstrip("\r\n")
|
decoded_data = raw.decode("UTF-8").rstrip("\r\n")
|
||||||
|
@ -141,7 +172,7 @@ class HTTPHandler:
|
||||||
decoded_data_split[0]: decoded_data_split[1].strip(" ")
|
decoded_data_split[0]: decoded_data_split[1].strip(" ")
|
||||||
})
|
})
|
||||||
|
|
||||||
def http_data_handle(self):
|
def __http_data_handle(self):
|
||||||
"""
|
"""
|
||||||
Chain of receive data partitions HTTP request
|
Chain of receive data partitions HTTP request
|
||||||
"""
|
"""
|
||||||
|
@ -161,7 +192,7 @@ class HTTPHandler:
|
||||||
return
|
return
|
||||||
|
|
||||||
log.debug("Want to receive {} bytes from client".format(bytes_to_receive))
|
log.debug("Want to receive {} bytes from client".format(bytes_to_receive))
|
||||||
self.data = self.read_fb.read(bytes_to_receive)
|
self.data = self.read_fd.read(bytes_to_receive)
|
||||||
|
|
||||||
log.debug("Request method: %s" % self.http_method)
|
log.debug("Request method: %s" % self.http_method)
|
||||||
log.debug("Requested address: %s" % self.http_address)
|
log.debug("Requested address: %s" % self.http_address)
|
||||||
|
@ -169,9 +200,9 @@ class HTTPHandler:
|
||||||
log.debug("Request in GET after ?: %s" % self.http_get_request)
|
log.debug("Request in GET after ?: %s" % self.http_get_request)
|
||||||
log.debug("Data: %s" % self.data)
|
log.debug("Data: %s" % self.data)
|
||||||
|
|
||||||
self.send_form_data()
|
self.__send_form_data()
|
||||||
|
|
||||||
def send_form_data(self):
|
def __send_form_data(self):
|
||||||
"""
|
"""
|
||||||
Chain of handle, pack & send data to client
|
Chain of handle, pack & send data to client
|
||||||
"""
|
"""
|
||||||
|
@ -185,10 +216,7 @@ class HTTPHandler:
|
||||||
if path.handler_type == HandlerType.STATIC_FILE:
|
if path.handler_type == HandlerType.STATIC_FILE:
|
||||||
log.debug("Address associated with static file on path {}".format(path.link))
|
log.debug("Address associated with static file on path {}".format(path.link))
|
||||||
r = Response(data=fileiter(path.link))
|
r = Response(data=fileiter(path.link))
|
||||||
|
self.__write_data(r)
|
||||||
for i in r:
|
|
||||||
self.write_fb.write(i)
|
|
||||||
self.write_fb.flush()
|
|
||||||
|
|
||||||
elif path.handler_type == HandlerType.FUNCTION:
|
elif path.handler_type == HandlerType.FUNCTION:
|
||||||
log.debug("Address associated with function")
|
log.debug("Address associated with function")
|
||||||
|
@ -206,44 +234,36 @@ class HTTPHandler:
|
||||||
elif func.__code__.co_argcount == 3:
|
elif func.__code__.co_argcount == 3:
|
||||||
func_return = func(self.http_headers, self.http_get_request, self.data)
|
func_return = func(self.http_headers, self.http_get_request, self.data)
|
||||||
|
|
||||||
for i in func_return:
|
self.__write_data(func_return)
|
||||||
self.write_fb.write(i)
|
|
||||||
self.write_fb.flush()
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
log.warning("Address configured on server, but type not allowed URL type in URLs list")
|
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:
|
r = Response(status_code=404,
|
||||||
self.write_fb.write(i)
|
data=b"Address configured on server, but type not allowed URL type in URLs list")
|
||||||
self.write_fb.flush()
|
self.__write_data(r)
|
||||||
|
|
||||||
if not found:
|
if not found:
|
||||||
r = Response(status_code=404, data=b"Not found!")
|
r = Response(status_code=404, data=b"Not found!")
|
||||||
|
self.__write_func(r)
|
||||||
|
|
||||||
for i in r:
|
self.__close()
|
||||||
self.write_fb.write(i)
|
|
||||||
self.write_fb.flush()
|
|
||||||
|
|
||||||
self.write_fb.close()
|
def init_server_socket():
|
||||||
self.read_fb.close()
|
|
||||||
self.conn.close()
|
|
||||||
|
|
||||||
|
|
||||||
def start_http_server():
|
|
||||||
try:
|
try:
|
||||||
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as main_server_socket:
|
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.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
|
||||||
|
log.debug("Attempt to bind socket to 0.0.0.0:{}".format(config.SETUP["server"]["port"]))
|
||||||
main_server_socket.bind(("0.0.0.0", config.SETUP["server"]["port"]))
|
main_server_socket.bind(("0.0.0.0", config.SETUP["server"]["port"]))
|
||||||
|
log.debug("Make socket is listen")
|
||||||
main_server_socket.listen()
|
main_server_socket.listen()
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
conn, addr = main_server_socket.accept()
|
conn, addr = main_server_socket.accept()
|
||||||
log.info("Accept connection from %s", addr[0])
|
log.info("Accepted connection from %s", addr[0])
|
||||||
|
|
||||||
thr_serv_conn = threading.Thread(target=HTTPHandler, args=(conn, addr,))
|
thr_serv_conn = threading.Thread(target=HTTPHandler, args=(conn, addr,), daemon=True)
|
||||||
thr_serv_conn.run()
|
thr_serv_conn.run()
|
||||||
|
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
log.info("Server get keyboard interrupt. Initialize server to stop")
|
log.info("Server get keyboard interrupt, bye!")
|
||||||
log.info("==================== End of log ====================")
|
|
||||||
|
|
|
@ -4,8 +4,14 @@ log = logging.getLogger(__name__)
|
||||||
|
|
||||||
STATUS_BY_CODE = {
|
STATUS_BY_CODE = {
|
||||||
200: "OK",
|
200: "OK",
|
||||||
|
|
||||||
|
400: "Bad Request",
|
||||||
404: "Not Found",
|
404: "Not Found",
|
||||||
|
405: "Method Not Allowed",
|
||||||
|
413: "Payload Too Large",
|
||||||
|
|
||||||
500: "Internal Server Error",
|
500: "Internal Server Error",
|
||||||
|
505: "HTTP Version Not Supported",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue