about summary refs log tree commit diff
diff options
context:
space:
mode:
authorUltraQbik <no1skill@yandex.ru>2024-08-28 19:15:55 +0300
committerUltraQbik <no1skill@yandex.ru>2024-08-28 19:15:55 +0300
commitce5bea2e653ed77e05022942904ced79553604b9 (patch)
tree2f9351e3d70ce9c11f2733ee7b2bf65b46a979cb
parent137c89169d722b9ec92b16f8e545661de502d6c2 (diff)
downloadhttpy-ce5bea2e653ed77e05022942904ced79553604b9.tar.gz
httpy-ce5bea2e653ed77e05022942904ced79553604b9.zip
Separate functions and use non-blocking sockets
-rw-r--r--main.py91
1 files changed, 53 insertions, 38 deletions
diff --git a/main.py b/main.py
index 3bfdeeb..7530dfc 100644
--- a/main.py
+++ b/main.py
@@ -46,6 +46,10 @@ _parser.add_argument("--disable-ssl",
                      help="SSL for HTTPs encrypted connection (default True)",
                      default=False,
                      action="store_true")
+_parser.add_argument("-v", "--verbose",
+                     help="verbose (default False)",
+                     default=False,
+                     action="store_true")
 ARGS = _parser.parse_args()
 
 # logging
@@ -154,17 +158,11 @@ class HTTPServer:
         """
 
         self.semaphore.acquire()
-        client.setblocking(True)  # ensures that client sockets are blocking
-        try:
-            request = self._recv_request(client)
-            if request is not None:
-                self._client_request_handler(client, request)
-        except ssl.SSLError:
-            pass
-        except OSError as e:
-            logging.info(f"request dropped due to: {e}")
-        except Exception:
-            logging.warning(f"ignoring exception:\n{traceback.format_exc()}")
+        client.setblocking(False)
+        request = self._recv_request(client)
+        logging.info(f"ip: {client.getpeername()[0]}\n{request}")
+        if request is not None:
+            self._client_request_handler(client, request)
 
         # Remove self from thread list and close the connection
         self.client_threads.remove(threading.current_thread())
@@ -184,24 +182,7 @@ class HTTPServer:
             case _:
                 response = Response(b'', STATUS_CODE_NOT_FOUND)
 
-        # process header data
-        if response.headers.get("Connection") is None:
-            response.headers["Connection"] = "close"
-
-        # generate basic message
-        message = b'HTTP/1.1 ' + response.status.__bytes__() + b'\r\n'
-        for key, value in response.headers.items():
-            message += f"{key}: {value}\r\n".encode("utf8")
-        message += b'\r\n'
-
-        # send message
-        client.sendall(message)
-        for packet in response.get_data_stream():
-            client.sendall(packet)
-
-            # check for stop event
-            if self.stop_event.is_set():
-                break
+        self._send_response(client, response)
 
     def _handle_get(self, client: _usocket, request: Request) -> Response:
         """
@@ -239,16 +220,46 @@ class HTTPServer:
         """
 
         buffer = bytearray()
+        size = 0
         while not self.stop_event.is_set():
-            if len(msg := client.recv(BUFFER_LENGTH)) == 0:
-                break
-            buffer += msg
-            if buffer[-4:] == b'\r\n\r\n':
-                return Request.create(buffer)
-            if len(buffer) > BUFFER_MAX_SIZE:  # ignore big messages
-                break
+            try:
+                buffer += client.recv(BUFFER_LENGTH)
+                if buffer[-4:] == b'\r\n\r\n':
+                    return Request.create(buffer)
+                if size == len(buffer):  # got 0 bytes
+                    break
+                if size > BUFFER_MAX_SIZE:
+                    break
+                size = len(buffer)
+            except (ssl.SSLWantReadError, BlockingIOError):
+                time.sleep(0.005)
         return None
 
+    def _send_response(self, client: _usocket, response: Response) -> None:
+        """
+        Send response to client
+        """
+
+        response.headers["Connection"] = "close"
+
+        # generate basic message
+        message = b'HTTP/1.1 ' + response.status.__bytes__() + b'\r\n'
+        for key, value in response.headers.items():
+            message += f"{key}: {value}\r\n".encode("utf8")
+        message += b'\r\n'
+
+        # send message
+        blk = client.getblocking()
+        client.setblocking(True)  # easier to transmit
+        client.sendall(message)
+        for packet in response.get_data_stream():
+            client.sendall(packet)
+
+            # check for stop event
+            if self.stop_event.is_set():
+                break
+        client.setblocking(blk)
+
     def _accept(self) -> _usocket | None:
         """
         socket.accept, but for more graceful closing
@@ -287,9 +298,13 @@ class HTTPServer:
 
 
 def main():
-    path_map = file_man.generate_path_map()
+    path_map = file_man.generate_path_map(verbose=ARGS.verbose)
     if not ARGS.dont_compress_path:
-        path_map = file_man.compress_path_map(path_map, path_prefix=ARGS.compressed_path, regen=True)
+        path_map = file_man.compress_path_map(
+            path_map,
+            path_prefix=ARGS.compressed_path,
+            regen=True,
+            verbose=ARGS.verbose)
 
     server = HTTPServer(
         port=ARGS.port,