feat(WebSocket): implement WebSocket handling and upgrade support
This commit is contained in:
parent
e2197c33fd
commit
ff664fa9d3
58
webdav3.py
58
webdav3.py
|
|
@ -43,7 +43,7 @@ from http.server import HTTPServer, BaseHTTPRequestHandler
|
||||||
from socketserver import ThreadingMixIn
|
from socketserver import ThreadingMixIn
|
||||||
import urllib.request, urllib.parse, urllib.error, urllib.parse
|
import urllib.request, urllib.parse, urllib.error, urllib.parse
|
||||||
from time import timezone, strftime, localtime, gmtime
|
from time import timezone, strftime, localtime, gmtime
|
||||||
import os, sys, re, shutil, uuid, hashlib, mimetypes, base64, socket
|
import os, sys, re, shutil, struct, uuid, hashlib, mimetypes, base64, socket
|
||||||
|
|
||||||
# Debug message ( True / False )
|
# Debug message ( True / False )
|
||||||
sys_debug = False
|
sys_debug = False
|
||||||
|
|
@ -687,7 +687,63 @@ class DAVRequestHandler(BaseHTTPRequestHandler):
|
||||||
self.end_headers()
|
self.end_headers()
|
||||||
w.flush()
|
w.flush()
|
||||||
|
|
||||||
|
def _ws_recv(self, sock, n):
|
||||||
|
data = b''
|
||||||
|
while len(data) < n:
|
||||||
|
chunk = sock.recv(n - len(data))
|
||||||
|
if not chunk:
|
||||||
|
raise ConnectionError('closed')
|
||||||
|
data += chunk
|
||||||
|
return data
|
||||||
|
|
||||||
|
def _handle_websocket(self):
|
||||||
|
key = self.headers.get('Sec-WebSocket-Key', '')
|
||||||
|
accept = base64.b64encode(
|
||||||
|
hashlib.sha1((key + '258EAFA5-E914-47DA-95CA-C5AB0DC85B11').encode()).digest()
|
||||||
|
).decode()
|
||||||
|
self.send_response(101, 'Switching Protocols')
|
||||||
|
self.send_header('Upgrade', 'websocket')
|
||||||
|
self.send_header('Connection', 'Upgrade')
|
||||||
|
self.send_header('Sec-WebSocket-Accept', accept)
|
||||||
|
self.end_headers()
|
||||||
|
sock = self.connection
|
||||||
|
sock.settimeout(None)
|
||||||
|
try:
|
||||||
|
while True:
|
||||||
|
b0, b1 = self._ws_recv(sock, 2)
|
||||||
|
opcode = b0 & 0x0F
|
||||||
|
masked = bool(b1 & 0x80)
|
||||||
|
length = b1 & 0x7F
|
||||||
|
if length == 126:
|
||||||
|
length = struct.unpack('>H', self._ws_recv(sock, 2))[0]
|
||||||
|
elif length == 127:
|
||||||
|
length = struct.unpack('>Q', self._ws_recv(sock, 8))[0]
|
||||||
|
mask = self._ws_recv(sock, 4) if masked else b''
|
||||||
|
payload = self._ws_recv(sock, length)
|
||||||
|
if masked:
|
||||||
|
payload = bytes(b ^ mask[i % 4] for i, b in enumerate(payload))
|
||||||
|
if opcode == 0x8: # close
|
||||||
|
sock.send(b'\x88\x00')
|
||||||
|
break
|
||||||
|
elif opcode == 0x9: # ping → pong
|
||||||
|
frame = bytes([0x8A, len(payload)]) + payload
|
||||||
|
sock.send(frame)
|
||||||
|
elif opcode in (0x1, 0x2): # text or binary — echo back as text
|
||||||
|
if length < 126:
|
||||||
|
header = bytes([0x81, length])
|
||||||
|
elif length < 65536:
|
||||||
|
header = bytes([0x81, 126]) + struct.pack('>H', length)
|
||||||
|
else:
|
||||||
|
header = bytes([0x81, 127]) + struct.pack('>Q', length)
|
||||||
|
sock.send(header + payload)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
def do_GET(self, onlyhead=False):
|
def do_GET(self, onlyhead=False):
|
||||||
|
if (self.path == '/ws'
|
||||||
|
and self.headers.get('Upgrade', '').lower() == 'websocket'):
|
||||||
|
self._handle_websocket()
|
||||||
|
return
|
||||||
if self.WebAuth():
|
if self.WebAuth():
|
||||||
return
|
return
|
||||||
path, elem = self.path_elem()
|
path, elem = self.path_elem()
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user