3333# mypy: disable-error-code="attr-defined"
3434
3535import asyncio
36- import logging
3736import os
3837import socket
3938import socketserver
40- import struct
41- from typing import List , Tuple
39+ from typing import Tuple
4240
43- from pythonosc import osc_message_builder , slip
41+ from pythonosc import osc_message_builder
4442from pythonosc .dispatcher import Dispatcher
43+ from pythonosc .parsing .framing import (
44+ BINARY_LENGTH_FRAMING ,
45+ SLIP_FRAMING ,
46+ Framing ,
47+ FramingParser ,
48+ )
4549
46- LOG = logging .getLogger ()
4750MODE_1_0 = "1.0"
4851MODE_1_1 = "1.1"
52+ FRAMING_BY_MODE : dict [str , Framing ] = {
53+ MODE_1_0 : BINARY_LENGTH_FRAMING ,
54+ MODE_1_1 : SLIP_FRAMING ,
55+ }
4956
5057
51- class _TCPHandler1_0 (socketserver .BaseRequestHandler ):
52- """Handles correct OSC1.0 messages.
58+ def get_framing (mode : str ) -> Framing :
59+ try :
60+ return FRAMING_BY_MODE [mode ]
61+ except KeyError :
62+ raise ValueError (f"Unsupported OSC mode: { mode } " ) from None
5363
54- Whether this will be run on its own thread, the server's or a whole new
55- process depends on the server you instantiated, look at their documentation.
5664
57- This method is called after a basic sanity check was done on the datagram,
58- basically whether this datagram looks like an osc message or bundle,
59- if not the server won't even bother to call it and so no new
60- threads/processes will be spawned.
61- """
62-
63- def handle (self ) -> None :
64- LOG .debug ("handle OSC 1.0 protocol" )
65- while True :
66- lengthbuf = self .recvall (4 )
67- if lengthbuf == b"" :
68- break
69- (length ,) = struct .unpack ("!I" , lengthbuf )
70- data = self .recvall (length )
71- if data == b"" :
72- break
73-
74- resp = self .server .dispatcher .call_handlers_for_packet (
75- data , self .client_address
76- )
77- # resp = _call_handlers_for_packet(data, self.server.dispatcher)
78- for r in resp :
79- if not isinstance (r , tuple ):
80- r = [r ]
81- msg = osc_message_builder .build_msg (r [0 ], r [1 :])
82- b = struct .pack ("!I" , len (msg .dgram ))
83- self .request .sendall (b + msg .dgram )
84-
85- def recvall (self , count : int ) -> bytes :
86- buf = b""
87- while count > 0 :
88- newbuf = self .request .recv (count )
89- if not newbuf :
90- return b""
91- buf += newbuf
92- count -= len (newbuf )
93- return buf
94-
95-
96- class _TCPHandler1_1 (socketserver .BaseRequestHandler ):
97- """Handles correct OSC1.1 messages.
65+ class TCPHandler (socketserver .BaseRequestHandler ):
66+ """Handles correct OSC messages.
9867
9968 Whether this will be run on its own thread, the server's or a whole new
10069 process depends on the server you instantiated, look at their documentation.
@@ -106,42 +75,28 @@ class _TCPHandler1_1(socketserver.BaseRequestHandler):
10675 """
10776
10877 def handle (self ) -> None :
109- LOG .debug ("handle OSC 1.1 protocol" )
110- while True :
111- packets = self .recvall ()
112- if not packets :
113- break
114-
115- for p in packets :
116- # resp = _call_handlers_for_packet(p, self.server.dispatcher)
78+ assert isinstance (self .server , OSCTCPServer )
79+
80+ parser = FramingParser (self .server .framing )
81+
82+ while chunk := self .request .recv (16384 ):
83+ for p in parser .feed (chunk ):
11784 resp = self .server .dispatcher .call_handlers_for_packet (
11885 p , self .client_address
11986 )
12087 for r in resp :
12188 if not isinstance (r , tuple ):
12289 r = [r ]
12390 msg = osc_message_builder .build_msg (r [0 ], r [1 :])
124- self .request .sendall (slip .encode (msg .dgram ))
125-
126- def recvall (self ) -> List [bytes ]:
127- buf = self .request .recv (4096 )
128- if not buf :
129- return []
130- # If the last byte is not an END marker there could be more data coming
131- while buf [- 1 ] != 192 :
132- newbuf = self .request .recv (4096 )
133- if not newbuf :
134- # Maybe should raise an exception here?
135- break
136- buf += newbuf
137-
138- packets = [slip .decode (p ) for p in buf .split (slip .END_END )]
139- return packets
91+ self .request .sendall (self .server .framing .encode (msg .dgram ))
14092
14193
14294class OSCTCPServer (socketserver .TCPServer ):
14395 """Superclass for different flavors of OSCTCPServer"""
14496
97+ dispatcher : Dispatcher
98+ framing : Framing
99+
145100 def __init__ (
146101 self ,
147102 server_address : Tuple [str | bytes | bytearray , int ],
@@ -150,9 +105,8 @@ def __init__(
150105 family : socket .AddressFamily | None = None ,
151106 ):
152107 self .request_queue_size = 300
153- self .mode = mode
154- if mode not in [MODE_1_0 , MODE_1_1 ]:
155- raise ValueError ("OSC Mode must be '1.0' or '1.1'" )
108+ self .dispatcher = dispatcher
109+ self .framing = get_framing (mode )
156110
157111 if family is not None :
158112 self .address_family = family
@@ -171,16 +125,7 @@ def __init__(
171125 # Fallback to default if resolution fails
172126 pass
173127
174- if self .mode == MODE_1_0 :
175- super ().__init__ (server_address , _TCPHandler1_0 )
176- else :
177- super ().__init__ (server_address , _TCPHandler1_1 )
178- self ._dispatcher = dispatcher
179-
180- @property
181- def dispatcher (self ):
182- """Dispatcher accessor for handlers to dispatch osc messages."""
183- return self ._dispatcher
128+ super ().__init__ (server_address , TCPHandler )
184129
185130
186131class BlockingOSCTCPServer (OSCTCPServer ):
@@ -230,8 +175,8 @@ def __init__(
230175 """
231176 self ._port = port
232177 self ._server_address = server_address
233- self ._dispatcher = dispatcher
234- self ._mode = mode
178+ self .dispatcher = dispatcher
179+ self .framing = get_framing ( mode )
235180
236181 async def __aenter__ (self ):
237182 return self
@@ -245,102 +190,41 @@ async def start(self) -> None:
245190 self .handle , self ._server_address , self ._port
246191 )
247192
248- addrs = ", " .join (str (sock .getsockname ()) for sock in self ._server .sockets )
249- LOG .debug (f"Serving on { addrs } " )
250-
251193 async with self ._server :
252194 await self ._server .serve_forever ()
253195
254196 async def stop (self ) -> None :
255197 self ._server .close ()
256198 await self ._server .wait_closed ()
257199
258- @property
259- def dispatcher (self ):
260- return self ._dispatcher
261-
262200 async def handle (
263201 self , reader : asyncio .StreamReader , writer : asyncio .StreamWriter
264202 ) -> None :
203+ try :
204+ await self ._handle (reader , writer )
205+ finally :
206+ if writer .can_write_eof ():
207+ writer .write_eof ()
208+ writer .close ()
209+ await writer .wait_closed ()
210+
211+ async def _handle (
212+ self , reader : asyncio .StreamReader , writer : asyncio .StreamWriter
213+ ) -> None :
214+ parser = FramingParser (self .framing )
265215 client_address = ("" , 0 )
266216 sock = writer .transport .get_extra_info ("socket" )
267217 if sock is not None :
268218 client_address = sock .getpeername ()
269219
270- if self ._mode == MODE_1_1 :
271- await self .handle_1_1 (reader , writer , client_address )
272- else :
273- await self .handle1_0 (reader , writer , client_address )
274- writer .write_eof ()
275- LOG .debug ("Close the connection" )
276- writer .close ()
277- await writer .wait_closed ()
278-
279- async def handle1_0 (
280- self ,
281- reader : asyncio .StreamReader ,
282- writer : asyncio .StreamWriter ,
283- client_address : Tuple [str , int ],
284- ) -> None :
285- LOG .debug ("Incoming socket open 1.0" )
286- while True :
287- try :
288- buf = await reader .read (4 )
289- except Exception as e :
290- LOG .exception ("Read error" , e )
291- return
292- if buf == b"" :
293- break
294- (length ,) = struct .unpack ("!I" , buf )
295- buf = b""
296- while length > 0 :
297- newbuf = await reader .read (length )
298- if not newbuf :
299- break
300- buf += newbuf
301- length -= len (newbuf )
302-
303- result = await self .dispatcher .async_call_handlers_for_packet (
304- buf , client_address
305- )
306- for r in result :
307- if not isinstance (r , tuple ):
308- r = [r ]
309- msg = osc_message_builder .build_msg (r [0 ], r [1 :])
310- b = struct .pack ("!I" , len (msg .dgram ))
311- writer .write (b + msg .dgram )
312- await writer .drain ()
313-
314- async def handle_1_1 (
315- self ,
316- reader : asyncio .StreamReader ,
317- writer : asyncio .StreamWriter ,
318- client_address : Tuple [str , int ],
319- ) -> None :
320- LOG .debug ("Incoming socket open 1.1" )
321- while True :
322- try :
323- buf = await reader .read (4096 )
324- except Exception as e :
325- LOG .exception ("Read error" , e )
326- return
327- if buf == b"" :
328- break
329- while len (buf ) > 0 and buf [- 1 ] != 192 :
330- newbuf = await reader .read (4096 )
331- if not newbuf :
332- # Maybe should raise an exception here?
333- break
334- buf += newbuf
335-
336- packets = [slip .decode (p ) for p in buf .split (slip .END_END )]
337- for p in packets :
220+ while chunk := await reader .read (16384 ):
221+ for p in parser .feed (chunk ):
338222 result = await self .dispatcher .async_call_handlers_for_packet (
339223 p , client_address
340224 )
341225 for r in result :
342226 if not isinstance (r , tuple ):
343227 r = [r ]
344228 msg = osc_message_builder .build_msg (r [0 ], r [1 :])
345- writer .write (slip .encode (msg .dgram ))
229+ writer .write (self . framing .encode (msg .dgram ))
346230 await writer .drain ()
0 commit comments