diff --git a/webdav3.py b/webdav3.py index 46f033c..2e9e46c 100644 --- a/webdav3.py +++ b/webdav3.py @@ -51,6 +51,56 @@ _ws_clients: set = set() # connected WebSocket sockets # Debug message ( True / False ) sys_debug = False +# ── Logging ──────────────────────────────────────────────────────────────────── + +def _log(tag: str, msg: str): + print(f'[{strftime("%H:%M:%S")}] [{tag}] {msg}', flush=True) + +# ── WebSocket broadcast (module-level so the REPL can call it) ───────────────── + +def ws_broadcast(text: str) -> int: + """Send a UTF-8 text frame to every connected WebSocket client.""" + payload = text.encode('utf-8') + n = len(payload) + if n < 126: + header = bytes([0x81, n]) + elif n < 65536: + header = bytes([0x81, 126]) + struct.pack('>H', n) + else: + header = bytes([0x81, 127]) + struct.pack('>Q', n) + frame = header + payload + with _ws_lock: + clients = set(_ws_clients) + sent = 0 + for sock in clients: + try: + sock.send(frame) + sent += 1 + except Exception: + with _ws_lock: + _ws_clients.discard(sock) + return sent + +# ── REPL ─────────────────────────────────────────────────────────────────────── + +def _repl_thread(): + """Read lines from stdin and broadcast them to all connected WS clients.""" + _log('REPL', 'ready — type a message and press Enter to broadcast, Ctrl-D to quit') + while True: + try: + sys.stdout.write('> ') + sys.stdout.flush() + line = sys.stdin.readline() + if not line: # EOF / Ctrl-D + break + line = line.rstrip('\r\n') + if not line: + continue + n = ws_broadcast(line) + _log('REPL', f'sent to {n} client(s): {line}') + except (EOFError, KeyboardInterrupt): + break + # get localhost IP address def get_localip(): try: @@ -704,6 +754,7 @@ class DAVRequestHandler(BaseHTTPRequestHandler): return message # echo by default def _ws_broadcast(self, payload: bytes): + # Thin wrapper kept for binary payloads; text goes through ws_broadcast(). with _ws_lock: clients = set(_ws_clients) for sock in clients: @@ -737,7 +788,7 @@ class DAVRequestHandler(BaseHTTPRequestHandler): sock.settimeout(None) with _ws_lock: _ws_clients.add(sock) - print(f'[WS] {client} connected (total: {len(_ws_clients)})') + _log('WS', f'{client} connected (clients: {len(_ws_clients)})') try: while True: b0, b1 = self._ws_recv(sock, 2) @@ -753,23 +804,23 @@ class DAVRequestHandler(BaseHTTPRequestHandler): if masked: payload = bytes(b ^ mask[i % 4] for i, b in enumerate(payload)) if opcode == 0x8: # close - print(f'[WS] {client} closed') + _log('WS', f'{client} close frame') sock.send(b'\x88\x00') break elif opcode == 0x9: # ping → pong sock.send(bytes([0x8A, len(payload)]) + payload) elif opcode in (0x1, 0x2): # text or binary message = payload.decode('utf-8', errors='replace') - print(f'[WS] {client} recv ({length}b): {message}') + _log('WS', f'{client} recv: {message}') response = self.ws_process_message(message) if response is not None: - self._ws_broadcast(response.encode('utf-8')) + ws_broadcast(response) except Exception: pass finally: with _ws_lock: _ws_clients.discard(sock) - print(f'[WS] {client} disconnected (total: {len(_ws_clients)})') + _log('WS', f'{client} disconnected (clients: {len(_ws_clients)})') def do_GET(self, onlyhead=False): if (self.path == '/ws' @@ -902,9 +953,16 @@ class DAVRequestHandler(BaseHTTPRequestHandler): break return (path, elem) - # disable log info output to screen - def log_message(self,format,*args): - pass + def log_message(self, format, *args): + # args: requestline, status_code, content_length (from log_request) + try: + parts = args[0].split() # e.g. ['GET', '/path', 'HTTP/1.1'] + method = parts[0] + path = urllib.parse.unquote(parts[1]) if len(parts) > 1 else '?' + status = args[1] + _log('DAV', f'{method} {path} → {status}') + except Exception: + _log('DAV', format % args) # fallback for unexpected call shapes class BufWriter: def __init__(self, w, debug=False): @@ -964,7 +1022,9 @@ if __name__ == '__main__': # **** Change First ./ to your dir , etc :/mnt/flash/public, d:/share_file/ root = DirCollection('files/', '/') httpd = DAVServer(server_address, DAVRequestHandler, root, userpwd) + repl = threading.Thread(target=_repl_thread, daemon=True, name='ws-repl') + repl.start() try: - httpd.serve_forever() # todo: add some control over starting and stopping the server + httpd.serve_forever() except: - print('# WebDav Server Stop.') \ No newline at end of file + _log('SRV', 'stopped') \ No newline at end of file