Receive data from client
Allow handle client requests with HTTP body for custom functions. - Rewrited "path" function to class; - Simple check clien request of method allowed; - Use "Enumirations" for handler types, HTTP methods;
This commit is contained in:
parent
c7413887cc
commit
9155cb17ed
5 changed files with 128 additions and 53 deletions
11
config.py
11
config.py
|
@ -1,9 +1,11 @@
|
||||||
import os
|
import os
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from server.urlhandler import path
|
from server.common import HandlerType, HTTPMethod
|
||||||
|
from server.urlhandler import Path
|
||||||
import testmod
|
import testmod
|
||||||
|
|
||||||
|
|
||||||
SETUP = {
|
SETUP = {
|
||||||
"setup": {
|
"setup": {
|
||||||
"log_to_file": False,
|
"log_to_file": False,
|
||||||
|
@ -14,6 +16,7 @@ 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"],
|
||||||
|
@ -22,7 +25,9 @@ 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="[%(name)s][%(asctime)s][%(levelname)s] %(message)s")
|
||||||
|
|
||||||
|
|
||||||
urls = [
|
urls = [
|
||||||
path("/", "static-file", "index.html"),
|
Path("/", HandlerType.STATIC_FILE, "index.html"),
|
||||||
path("/func", "function", testmod.work)
|
Path("/func", HandlerType.FUNCTION, testmod.work, HTTPMethod.GET|HTTPMethod.POST),
|
||||||
|
Path("/func1", HandlerType.FUNCTION, testmod.work2, HTTPMethod.POST)
|
||||||
]
|
]
|
||||||
|
|
19
server/common.py
Normal file
19
server/common.py
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
import enum
|
||||||
|
|
||||||
|
|
||||||
|
@enum.unique
|
||||||
|
class HandlerType(enum.Enum):
|
||||||
|
STATIC_FILE = 0
|
||||||
|
REDIRECT = 1
|
||||||
|
FUNCTION = 2
|
||||||
|
|
||||||
|
|
||||||
|
class HTTPMethod(enum.Flag):
|
||||||
|
HEAD = 1
|
||||||
|
GET = 2
|
||||||
|
POST = 4
|
||||||
|
PUT = 8
|
||||||
|
DELETE = 16
|
||||||
|
OPTIONS = 32
|
||||||
|
TRACE = 64
|
||||||
|
PATCH = 128
|
|
@ -7,6 +7,7 @@ import config
|
||||||
|
|
||||||
from .file_read import fileiter
|
from .file_read import fileiter
|
||||||
from .response import Response
|
from .response import Response
|
||||||
|
from .common import HandlerType, HTTPMethod
|
||||||
|
|
||||||
|
|
||||||
MAX_REQLINE = 64 * 1024
|
MAX_REQLINE = 64 * 1024
|
||||||
|
@ -22,7 +23,7 @@ class HTTPHandler:
|
||||||
self.read_fb = conn.makefile("rb")
|
self.read_fb = conn.makefile("rb")
|
||||||
self.write_fb = conn.makefile("wb")
|
self.write_fb = conn.makefile("wb")
|
||||||
|
|
||||||
self.http_type = ""
|
self.http_method = ""
|
||||||
self.http_address = ""
|
self.http_address = ""
|
||||||
self.http_get_request = {}
|
self.http_get_request = {}
|
||||||
self.http_headers = {}
|
self.http_headers = {}
|
||||||
|
@ -33,7 +34,7 @@ class HTTPHandler:
|
||||||
|
|
||||||
def pre_handle_connection(self):
|
def pre_handle_connection(self):
|
||||||
"""
|
"""
|
||||||
Chain of handle first line in HTTP request
|
Chain of handle first line of HTTP request
|
||||||
"""
|
"""
|
||||||
raw = self.read_fb.readline(MAX_REQLINE + 1)
|
raw = self.read_fb.readline(MAX_REQLINE + 1)
|
||||||
|
|
||||||
|
@ -44,6 +45,7 @@ class HTTPHandler:
|
||||||
return
|
return
|
||||||
|
|
||||||
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:
|
||||||
|
@ -51,27 +53,55 @@ class HTTPHandler:
|
||||||
log.debug("Request head too long")
|
log.debug("Request head too long")
|
||||||
else:
|
else:
|
||||||
log.debug("Request head too small")
|
log.debug("Request head too small")
|
||||||
|
|
||||||
self.conn.send(b"")
|
self.conn.send(b"")
|
||||||
self.conn.close()
|
self.conn.close()
|
||||||
return
|
return
|
||||||
|
|
||||||
if (req_line_splited[2] == "HTTP/1.0" or
|
if (req_line_splited[2] == "HTTP/1.0" or
|
||||||
req_line_splited[2] == "HTTP/1.1"):
|
req_line_splited[2] == "HTTP/1.1"):
|
||||||
self.http_type = req_line_splited[0]
|
|
||||||
|
http_method = req_line_splited[0]
|
||||||
|
|
||||||
else:
|
else:
|
||||||
self.conn.send(b"")
|
self.conn.send(b"")
|
||||||
self.conn.close()
|
self.conn.close()
|
||||||
return
|
return
|
||||||
|
|
||||||
if req_line_splited[0] == "GET" and req_line_splited[1].find("?") >= 0:
|
if http_method == "GET":
|
||||||
|
self.http_method = HTTPMethod.GET
|
||||||
|
elif http_method == "POST":
|
||||||
|
self.http_method = HTTPMethod.POST
|
||||||
|
elif http_method == "PUT":
|
||||||
|
self.http_method = HTTPMethod.PUT
|
||||||
|
elif http_method == "HEAD":
|
||||||
|
self.http_method = HTTPMethod.HEAD
|
||||||
|
elif http_method == "DELETE":
|
||||||
|
self.http_method = HTTPMethod.DELETE
|
||||||
|
elif http_method == "OPTIONS":
|
||||||
|
self.http_method = HTTPMethod.OPTIONS
|
||||||
|
elif http_method == "TRACE":
|
||||||
|
self.http_method = HTTPMethod.TRACE
|
||||||
|
elif http_method == "PATCH":
|
||||||
|
self.http_method = HTTPMethod.PATCH
|
||||||
|
else:
|
||||||
|
self.conn.send(b"")
|
||||||
|
self.conn.close()
|
||||||
|
return
|
||||||
|
|
||||||
|
if (self.http_method == HTTPMethod.GET and
|
||||||
|
req_line_splited[1].find("?") >= 0):
|
||||||
|
|
||||||
addr, get_req = req_line_splited[1].split("?", 1)
|
addr, get_req = req_line_splited[1].split("?", 1)
|
||||||
self.http_address = addr
|
self.http_address = addr
|
||||||
|
|
||||||
for param in get_req.split("&"):
|
for param in get_req.split("&"):
|
||||||
if len(param) == 0:
|
if len(param) == 0:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
elif param.find("=") >= 0:
|
elif param.find("=") >= 0:
|
||||||
name, val = param.split("=", 1)
|
name, val = param.split("=", 1)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
name, val = (param, "")
|
name, val = (param, "")
|
||||||
|
|
||||||
|
@ -116,22 +146,24 @@ class HTTPHandler:
|
||||||
Chain of receive data partitions HTTP request
|
Chain of receive data partitions HTTP request
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# TODO: Get from headers Content-Length and get body of request, else
|
if "Content-Length" in self.http_headers:
|
||||||
# receive 1024 bytes
|
# TODO: if content-length biggest of MAX_REQLINE - partition receive
|
||||||
"""
|
log.debug("Request has contain body, try to read")
|
||||||
raw = self.read_fb.readline(1024)
|
log.debug("Check Content-Length value type:")
|
||||||
|
|
||||||
if len(raw) == 1024:
|
if self.http_headers["Content-Length"].isdigit():
|
||||||
while True:
|
log.debug(" Pass")
|
||||||
self.data += raw
|
bytes_to_receive = int(self.http_headers["Content-Length"])
|
||||||
|
else:
|
||||||
|
log.error(" Content-Length is not integer")
|
||||||
|
self.conn.send(b"")
|
||||||
|
self.conn.close()
|
||||||
|
return
|
||||||
|
|
||||||
if len(raw) < 1024:
|
log.debug("Want to receive {} bytes from client".format(bytes_to_receive))
|
||||||
break
|
self.data = self.read_fb.read(bytes_to_receive)
|
||||||
else:
|
|
||||||
raw = self.read_fb.readline(1024)
|
|
||||||
"""
|
|
||||||
|
|
||||||
log.debug("Type of request: %s" % self.http_type)
|
log.debug("Request method: %s" % self.http_method)
|
||||||
log.debug("Requested address: %s" % self.http_address)
|
log.debug("Requested address: %s" % self.http_address)
|
||||||
log.debug("Request headers: %s" % self.http_headers)
|
log.debug("Request headers: %s" % self.http_headers)
|
||||||
log.debug("Request in GET after ?: %s" % self.http_get_request)
|
log.debug("Request in GET after ?: %s" % self.http_get_request)
|
||||||
|
@ -145,33 +177,36 @@ class HTTPHandler:
|
||||||
"""
|
"""
|
||||||
found = False
|
found = False
|
||||||
|
|
||||||
for elem in config.urls:
|
for path in config.urls:
|
||||||
if self.http_address in elem.keys():
|
if self.http_address == path.address:
|
||||||
url_metadata = elem[self.http_address]
|
if self.http_method in path.methods:
|
||||||
|
|
||||||
if self.http_type in url_metadata[0]:
|
|
||||||
found = True
|
found = True
|
||||||
|
|
||||||
if url_metadata[1] == "static-file":
|
if path.handler_type == HandlerType.STATIC_FILE:
|
||||||
r = Response(data=fileiter(url_metadata[2]))
|
log.debug("Address associated with static file on path {}".format(path.link))
|
||||||
|
r = Response(data=fileiter(path.link))
|
||||||
|
|
||||||
for i in r:
|
for i in r:
|
||||||
self.write_fb.write(i)
|
self.write_fb.write(i)
|
||||||
self.write_fb.flush()
|
self.write_fb.flush()
|
||||||
|
|
||||||
elif url_metadata[1] == "function":
|
elif path.handler_type == HandlerType.FUNCTION:
|
||||||
func = url_metadata[2]
|
log.debug("Address associated with function")
|
||||||
|
func = path.link
|
||||||
|
|
||||||
if func.__code__.co_argcount == 0:
|
if func.__code__.co_argcount == 0:
|
||||||
func_ret = func()
|
func_return = func()
|
||||||
|
|
||||||
elif func.__code__.co_argcount == 1:
|
elif func.__code__.co_argcount == 1:
|
||||||
func_ret = func(self.http_headers)
|
func_return = func(self.http_headers)
|
||||||
|
|
||||||
elif func.__code__.co_argcount == 2:
|
elif func.__code__.co_argcount == 2:
|
||||||
func_ret = func(self.http_headers, self.http_get_request)
|
func_return = func(self.http_headers, self.http_get_request)
|
||||||
|
|
||||||
for i in func_ret:
|
elif func.__code__.co_argcount == 3:
|
||||||
|
func_return = func(self.http_headers, self.http_get_request, self.data)
|
||||||
|
|
||||||
|
for i in func_return:
|
||||||
self.write_fb.write(i)
|
self.write_fb.write(i)
|
||||||
self.write_fb.flush()
|
self.write_fb.flush()
|
||||||
|
|
||||||
|
|
|
@ -1,29 +1,40 @@
|
||||||
import os
|
|
||||||
import logging
|
import logging
|
||||||
|
import os
|
||||||
|
|
||||||
|
from .common import HandlerType, HTTPMethod
|
||||||
|
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def path(path="/", _type="static-file", link="index.html", methods=("GET",)):
|
class Path:
|
||||||
log.debug("Start generate path for %s address" % path)
|
def __init__(self, address, handler_type=HandlerType.STATIC_FILE, link="index.html", methods=HTTPMethod.GET):
|
||||||
|
self.address = address
|
||||||
|
self.handler_type = handler_type
|
||||||
|
self.link = link
|
||||||
|
self.methods = methods
|
||||||
|
|
||||||
if _type == "static-file":
|
def check_method(self, method):
|
||||||
log.info("Path %s is %s and linked %s for methods %s" % (path, _type, link, methods))
|
return method in self.methods
|
||||||
return {
|
|
||||||
path: (methods, _type, os.path.join(os.getcwd(), link))
|
|
||||||
}
|
|
||||||
|
|
||||||
elif _type == "redirect":
|
@property
|
||||||
log.info("Path %s is %s and linked %s for methods %s" % (path, _type, link, methods))
|
def handler_type(self):
|
||||||
return {
|
return self._handler_type
|
||||||
path: (methods, _type, link)
|
|
||||||
}
|
|
||||||
|
|
||||||
elif _type == "function":
|
@handler_type.setter
|
||||||
log.info("Path %s is %s and linked %s for methods %s" % (path, _type, link, methods))
|
def handler_type(self, value):
|
||||||
return {
|
if not isinstance(value, HandlerType):
|
||||||
path: (methods, _type, link)
|
raise ValueError
|
||||||
}
|
|
||||||
else:
|
self._handler_type = value
|
||||||
log.error("Path %s is %s but it type is unknown" % (path, _type))
|
|
||||||
|
@property
|
||||||
|
def methods(self):
|
||||||
|
return self._methods
|
||||||
|
|
||||||
|
@methods.setter
|
||||||
|
def methods(self, value):
|
||||||
|
if not isinstance(value, HTTPMethod):
|
||||||
|
raise ValueError
|
||||||
|
|
||||||
|
self._methods = value
|
||||||
|
|
|
@ -5,3 +5,8 @@ def work(headers, request):
|
||||||
print(headers, request)
|
print(headers, request)
|
||||||
|
|
||||||
return Response(data=b"Tested!")
|
return Response(data=b"Tested!")
|
||||||
|
|
||||||
|
def work2(headers, get_request, post_request):
|
||||||
|
print(headers, get_request, post_request)
|
||||||
|
|
||||||
|
return Response(data=b"Pass")
|
||||||
|
|
Loading…
Reference in a new issue