update
This commit is contained in:
@@ -1,12 +1,17 @@
|
||||
import logging
|
||||
from logging import Logger
|
||||
import mimetypes
|
||||
import os
|
||||
import sys
|
||||
from datetime import datetime
|
||||
from socket import socket
|
||||
from time import mktime
|
||||
from typing import Union
|
||||
from urllib.parse import ParseResultBytes, ParseResult
|
||||
from wsgiref.handlers import format_date_time
|
||||
|
||||
from httplib import parser
|
||||
from httplib.exceptions import MethodNotAllowed, BadRequest
|
||||
from httplib.httpsocket import HTTPSocket
|
||||
from httplib.exceptions import MethodNotAllowed, BadRequest, UnsupportedEncoding, NotImplemented, NotFound
|
||||
from httplib.httpsocket import HTTPSocket, FORMAT
|
||||
from httplib.retriever import Retriever
|
||||
|
||||
METHODS = ("GET", "HEAD", "PUT", "POST")
|
||||
@@ -14,44 +19,98 @@ METHODS = ("GET", "HEAD", "PUT", "POST")
|
||||
|
||||
class RequestHandler:
|
||||
conn: HTTPSocket
|
||||
logger: Logger
|
||||
root = os.path.join(os.path.dirname(sys.argv[0]), "public")
|
||||
|
||||
def __init__(self, conn: socket, logger, host):
|
||||
def __init__(self, conn: socket, host):
|
||||
self.conn = HTTPSocket(conn, host)
|
||||
self.logger = logger
|
||||
|
||||
def listen(self):
|
||||
self.logger.debug("Parsing request line")
|
||||
logging.debug("test logger")
|
||||
logging.debug("Parsing request line")
|
||||
(method, target, version) = parser.parse_request_line(self.conn)
|
||||
headers = parser.parse_request_headers(self.conn)
|
||||
|
||||
self._validate_request(method, target, version, headers)
|
||||
|
||||
self.logger.debug("Parsed request-line: version: %s, target: %r", method, target)
|
||||
headers = parser.get_headers(self.conn)
|
||||
self.logger.debug("Parsed headers: %r", headers)
|
||||
retriever = Retriever.create(self.conn, headers)
|
||||
body = retriever.retrieve()
|
||||
logging.debug("Parsed request-line: method: %s, target: %r", method, target)
|
||||
|
||||
self.logger.debug("body: %r", body)
|
||||
body = b""
|
||||
if self._has_body(headers):
|
||||
try:
|
||||
retriever = Retriever.create(self.conn, headers)
|
||||
except UnsupportedEncoding as e:
|
||||
logging.error("Encoding not supported: %s=%s", e.enc_type, e.encoding)
|
||||
raise NotImplemented()
|
||||
|
||||
for buffer in retriever.retrieve():
|
||||
body += buffer
|
||||
|
||||
# completed message
|
||||
self._handle_message(method, target.path, body)
|
||||
|
||||
def _check_request_line(self, method: str, target: Union[ParseResultBytes, ParseResult], version):
|
||||
|
||||
if method not in METHODS:
|
||||
raise MethodNotAllowed(METHODS)
|
||||
|
||||
# only origin-form and absolute-form are allowed
|
||||
if len(target.path) < 1 or target.path[0] != "/" or \
|
||||
target.netloc not in ("http", "https") and target.hostname == "":
|
||||
raise BadRequest()
|
||||
|
||||
if version not in ("1.0", "1.1"):
|
||||
raise BadRequest()
|
||||
|
||||
# only origin-form and absolute-form are allowed
|
||||
if target.scheme not in ("", "http"):
|
||||
# Only http is supported...
|
||||
raise BadRequest()
|
||||
if target.netloc != "" and target.netloc != self.conn.host and target.netloc != self.conn.host.split(":")[0]:
|
||||
raise NotFound()
|
||||
|
||||
if target.path == "" or target.path[0] != "/":
|
||||
raise NotFound()
|
||||
|
||||
norm_path = os.path.normpath(target.path)
|
||||
if not os.path.exists(self.root + norm_path):
|
||||
raise NotFound()
|
||||
|
||||
def _validate_request(self, method, target, version, headers):
|
||||
if version == "1.1" and "host" not in headers:
|
||||
raise BadRequest()
|
||||
|
||||
self._check_request_line(method, target, version)
|
||||
|
||||
if version == "1.1" and "host" not in headers:
|
||||
raise BadRequest()
|
||||
def _has_body(self, headers):
|
||||
return "transfer-encoding" in headers or "content-encoding" in headers
|
||||
|
||||
def _get_date(self):
|
||||
now = datetime.now()
|
||||
stamp = mktime(now.timetuple())
|
||||
return format_date_time(stamp)
|
||||
|
||||
def _handle_message(self, method: str, target, body: bytes):
|
||||
date = self._get_date()
|
||||
|
||||
if method == "GET":
|
||||
if target == "/":
|
||||
path = self.root + "/index.html"
|
||||
else:
|
||||
path = self.root + target
|
||||
mime = mimetypes.guess_type(path)[0]
|
||||
if mime.startswith("test"):
|
||||
file = open(path, "rb", FORMAT)
|
||||
else:
|
||||
file = open(path, "rb")
|
||||
buffer = file.read()
|
||||
file.close()
|
||||
|
||||
message = "HTTP/1.1 200 OK\r\n"
|
||||
message += date + "\r\n"
|
||||
if mime:
|
||||
message += f"Content-Type: {mime}"
|
||||
if mime.startswith("test"):
|
||||
message += "; charset=UTF-8"
|
||||
message += "\r\n"
|
||||
message += f"Content-Length: {len(buffer)}\r\n"
|
||||
message += "\r\n"
|
||||
message = message.encode(FORMAT)
|
||||
message += buffer
|
||||
message += b"\r\n"
|
||||
|
||||
logging.debug("Sending: %r", message)
|
||||
self.conn.conn.sendall(message)
|
||||
|
@@ -87,8 +87,9 @@ class HTTPServer:
|
||||
for i in range(self.worker_count):
|
||||
logging.debug("Creating worker: %d", i + 1)
|
||||
p = mp.Process(target=worker.worker,
|
||||
args=(self.address, i + 1, self.logging_level, self._dispatch_queue, self._stop_event))
|
||||
args=(f"{self.address}:{self.port}", i + 1, self.logging_level, self._dispatch_queue, self._stop_event))
|
||||
p.start()
|
||||
self.workers.append(p)
|
||||
|
||||
time.sleep(0.1)
|
||||
time.sleep(0.2)
|
||||
time.sleep(1)
|
||||
|
@@ -4,23 +4,22 @@ import multiprocessing as mp
|
||||
import threading
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
from logging import Logger
|
||||
from socket import socket
|
||||
import socket
|
||||
|
||||
from server.RequestHandler import RequestHandler
|
||||
|
||||
THREAD_LIMIT = 20
|
||||
THREAD_LIMIT = 128
|
||||
|
||||
|
||||
def worker(address, name, log_level, queue: mp.Queue, stop_event: mp.Event):
|
||||
logging.basicConfig(level=log_level)
|
||||
logger = multiprocessing.log_to_stderr(level=log_level)
|
||||
runner = Worker(address, name, logger, queue, stop_event)
|
||||
runner.logger.debug("Worker %s started", name)
|
||||
def worker(address, name, logging_level, queue: mp.Queue, stop_event: mp.Event):
|
||||
logging.basicConfig(level=logging_level, format="%(levelname)s:[WORKER " + str(name) + "] %(message)s")
|
||||
runner = Worker(address, name, queue, stop_event)
|
||||
logging.debug("started")
|
||||
|
||||
try:
|
||||
runner.run()
|
||||
except KeyboardInterrupt:
|
||||
logger.debug("Ctrl+C pressed, terminating")
|
||||
logging.debug("Ctrl+C pressed, terminating")
|
||||
runner.shutdown()
|
||||
|
||||
|
||||
@@ -35,10 +34,9 @@ class Worker:
|
||||
|
||||
finished_queue: mp.Queue
|
||||
|
||||
def __init__(self, host, name, logger, queue: mp.Queue, stop_event: mp.Event):
|
||||
def __init__(self, host, name, queue: mp.Queue, stop_event: mp.Event):
|
||||
self.host = host
|
||||
self.name = name
|
||||
self.logger = logger
|
||||
self.queue = queue
|
||||
self.executor = ThreadPoolExecutor(THREAD_LIMIT)
|
||||
self.stop_event = stop_event
|
||||
@@ -58,26 +56,27 @@ class Worker:
|
||||
if conn is None or addr is None:
|
||||
break
|
||||
|
||||
self.logger.debug("Received new client: %s", addr)
|
||||
logging.debug("Processing new client: %s", addr)
|
||||
|
||||
# submit client to thread
|
||||
print(threading.get_ident())
|
||||
self.executor.submit(self._handle_client, conn, addr)
|
||||
|
||||
self.shutdown()
|
||||
|
||||
def _handle_client(self, conn: socket, addr):
|
||||
def _handle_client(self, conn: socket.socket, addr):
|
||||
try:
|
||||
self.logger.debug("Handling client: %s", addr)
|
||||
logging.debug("Handling client: %s", addr)
|
||||
|
||||
handler = RequestHandler(conn, self.logger, self.host)
|
||||
handler = RequestHandler(conn, self.host)
|
||||
handler.listen()
|
||||
except Exception as e:
|
||||
self.logger.debug("Internal error", exc_info=e)
|
||||
logging.debug("Internal error")
|
||||
|
||||
conn.shutdown(socket.SHUT_RDWR)
|
||||
conn.close()
|
||||
# Finished, put back into queue
|
||||
self.finished_queue.put(threading.get_ident())
|
||||
|
||||
def shutdown(self):
|
||||
self.logger.info("shutting down")
|
||||
logging.info("shutting down")
|
||||
self.executor.shutdown()
|
||||
|
Reference in New Issue
Block a user