55import itertools
66import json
77import logging
8+ import struct
89import time
910import uuid
10- from struct import pack , unpack
11+ from enum import IntEnum
1112
1213logger = logging .getLogger (__name__ )
1314
1415MAX_INT64 = (2 ** 64 - 1 )
1516
1617
18+ class FramedMessage :
19+ """
20+ A complete message constructed from one or more Frames.
21+ """
22+
23+ __slots__ = ("msg_id" , "header" , "payload" )
24+
25+ def __init__ (self , msg_id = None , header = None , payload = None ):
26+ if msg_id is None :
27+ msg_id = uuid .uuid4 ().int
28+ self .msg_id = msg_id
29+ self .header = header
30+ self .payload = payload
31+
32+
33+ def serialize (self ):
34+ h = json .dumps (self .header ).encode ("utf-8" )
35+ return b'' .join ([
36+ Frame .wrap (h , type_ = Frame .Types .HEADER , msg_id = self .msg_id ).serialize (),
37+ h ,
38+ Frame .wrap (self .payload , msg_id = self .msg_id ).serialize (),
39+ self .payload
40+ ])
41+
42+
43+ class CommandMessage (FramedMessage ):
44+
45+ def serialize (self ):
46+ h = json .dumps (self .header ).encode ("utf-8" )
47+ return b'' .join ([
48+ Frame .wrap (h , type_ = Frame .Types .COMMAND , msg_id = self .msg_id ).serialize (),
49+ h ,
50+ ])
51+
52+
1753class FramedBuffer :
1854 """
1955 A buffer that accumulates frames and bytes to produce a header and a
@@ -33,11 +69,11 @@ async def put(self, data):
3369 if not self .to_read :
3470 return await self .handle_frame (data )
3571 await self .consume (data )
36-
72+
3773 async def handle_frame (self , data ):
3874 self .current_frame , rest = Frame .from_data (data )
3975
40- if self .current_frame .type not in ( Frame .HEADER , Frame . PAYLOAD ) :
76+ if self .current_frame .type not in Frame .Types :
4177 raise Exception ("Unknown Frame Type" )
4278
4379 self .to_read = self .current_frame .length
@@ -50,11 +86,18 @@ async def consume(self, data):
5086 await self .finish ()
5187
5288 async def finish (self ):
53- if self .current_frame .type == Frame .HEADER :
54- self .header = Header .from_bytes (self .bb )
55- elif self .current_frame .type == Frame .PAYLOAD :
56- await self .q .put ((self .header , self .bb ))
89+ if self .current_frame .type == Frame .Types .HEADER :
90+ self .header = json .loads (self .bb )
91+ elif self .current_frame .type == Frame .Types .PAYLOAD :
92+ await self .q .put (FramedMessage (
93+ self .current_frame .msg_id , header = self .header ,
94+ payload = self .bb ))
5795 self .header = None
96+ elif self .current_frame .type == Frame .Types .COMMAND :
97+ await self .q .put (FramedMessage (
98+ self .current_frame .msg_id , header = json .loads (self .bb )))
99+ else :
100+ raise Exception ("Unknown Frame Type" )
58101 self .to_read = 0
59102 self .bb = bytearray ()
60103
@@ -63,11 +106,17 @@ async def get(self):
63106
64107
65108class Frame :
66- HEADER = 0
67- PAYLOAD = 1
109+
110+ class Types (IntEnum ):
111+ HEADER = 0
112+ PAYLOAD = 1
113+ COMMAND = 2
114+
115+ fmt = struct .Struct (">ccIIQQ" )
68116
69117 __slots__ = ('type' , 'version' , 'length' , 'msg_id' , 'id' )
70118
119+
71120 def __init__ (self , type_ , version , length , msg_id , id_ ):
72121 self .type = type_
73122 self .version = version
@@ -76,17 +125,29 @@ def __init__(self, type_, version, length, msg_id, id_):
76125 self .id = id_
77126
78127 def serialize (self ):
79- return pack (">ccIIQQ" , bytes ([self .type ]), bytes ([self .version ]), self .id , self .length , * split_uuid (self .msg_id ))
128+ return self .fmt .pack (
129+ bytes ([self .type ]), bytes ([self .version ]),
130+ self .id , self .length , * split_uuid (self .msg_id ))
80131
81132 @classmethod
82133 def deserialize (cls , buf ):
83- t , v , i , length , hi , lo = unpack (">ccIIQQ" , buf )
134+ t , v , i , length , hi , lo = Frame . fmt . unpack (buf )
84135 msg_id = join_uuid (hi , lo )
85- return cls (ord (t ), ord (v ), length , msg_id , i )
136+ return cls (Frame . Types ( ord (t ) ), ord (v ), length , msg_id , i )
86137
87138 @classmethod
88139 def from_data (cls , data ):
89- return cls .deserialize (data [:26 ]), data [26 :]
140+ return cls .deserialize (data [:Frame .fmt .size ]), data [Frame .fmt .size :]
141+
142+ @classmethod
143+ def wrap (cls , data , type_ = Types .PAYLOAD , msg_id = None ):
144+ """
145+ Returns a frame for the passed data.
146+ """
147+ if not msg_id :
148+ msg_id = uuid .uuid4 ().int
149+
150+ return cls (type_ , 1 , len (data ), msg_id , 1 )
90151
91152
92153def split_uuid (data ):
@@ -97,24 +158,10 @@ def join_uuid(hi, lo):
97158 return (hi << 64 ) | lo
98159
99160
100- class Header :
101- def __init__ (self , sender , recipient , route_list ):
102- self .sender = sender
103- self .recipient = recipient
104- self .route_list = route_list
105-
106- def serialize (self ):
107- return json .dumps ({"sender" : self .sender , "recipient" : self .recipient , "route_list" : self .route_list }).encode ("utf-8" )
108-
109- @classmethod
110- def from_bytes (cls , data ):
111- return cls (** json .loads (data ))
112-
113- def __repr__ (self ):
114- return f"Header({ self .sender } , { self .recipient } , { self .route_list } )"
115-
116- def __eq__ (self , other ):
117- return (self .sender , self .recipient , self .route_list ) == (other .sender , other .recipient , other .route_list )
161+ def glue (header , payload ):
162+ hf = Frame .wrap (header , type_ = Frame .Types .HEADER )
163+ pf = Frame .wrap (payload , msg_id = hf .msg_id )
164+ return hf .serialize () + header + pf .serialize () + payload
118165
119166
120167def gen_chunks (data , header , msg_id = None , chunksize = 2 ** 8 ):
@@ -123,9 +170,9 @@ def gen_chunks(data, header, msg_id=None, chunksize=2 ** 8):
123170 seq = itertools .count ()
124171 buf = bytearray (chunksize )
125172 bv = memoryview (buf )
126- header = header . serialize ( )
127- yield Frame (Frame .HEADER , 1 , len (header ), msg_id , next (seq )).serialize () + header
128- yield Frame (Frame .PAYLOAD , 1 , len (data ), msg_id , next (seq )).serialize ()
173+ header = json . dumps ( header ). encode ( "utf-8" )
174+ yield Frame (Frame .Types . HEADER , 1 , len (header ), msg_id , next (seq )).serialize () + header
175+ yield Frame (Frame .Types . PAYLOAD , 1 , len (data ), msg_id , next (seq )).serialize ()
129176 buffer = io .BytesIO (data )
130177 bytes_read = buffer .readinto (buf )
131178 while bytes_read :
@@ -146,13 +193,13 @@ def __init__(self, frame_id, sender, recipient, route_list, inner):
146193 self .inner_obj = None
147194
148195 async def deserialize_inner (self , receptor ):
149- self .inner_obj = await InnerEnvelope .deserialize (receptor , self .inner )
196+ self .inner_obj = await Inner .deserialize (receptor , self .inner )
150197
151198 @classmethod
152199 def from_raw (cls , raw ):
153200 doc = json .loads (raw )
154201 return cls (** doc )
155-
202+
156203 def serialize (self ):
157204 return json .dumps (dict (
158205 frame_id = self .frame_id ,
@@ -163,7 +210,7 @@ def serialize(self):
163210 ))
164211
165212
166- class InnerEnvelope :
213+ class Inner :
167214 def __init__ (self , receptor , message_id , sender , recipient , message_type , timestamp ,
168215 raw_payload , directive = None , in_response_to = None , ttl = None , serial = 1 ,
169216 code = 0 , expire_time_delta = 300 ):
0 commit comments