1 /*
2  * MsgTrans - Message Transport Framework for DLang. Based on TCP, WebSocket, UDP transmission protocol.
3  *
4  * Copyright (C) 2019 HuntLabs
5  *
6  * Website: https://www.msgtrans.org
7  *
8  * Licensed under the Apache-2.0 License.
9  *
10  */
11 
12 module msgtrans.PacketParser;
13 
14 import hunt.io.ByteBuffer;
15 import hunt.io.BufferUtils;
16 import hunt.collection.List;
17 import hunt.collection.ArrayList;
18 
19 import hunt.logging.ConsoleLogger;
20 import hunt.util.Serialize;
21 
22 import msgtrans.MessageBuffer;
23 import msgtrans.PacketHeader;
24 import std.bitmanip;
25 import std.algorithm : max, canFind;
26 
27 
28 /**
29  *
30  */
31 class PacketParser {
32     private ByteBuffer _receivedPacketBuf;
33     private size_t _defaultBufferSize = 8*1024;
34 
35     this(size_t bufferSize = 8*1024) {
36         _defaultBufferSize = bufferSize;
37     }
38 
39     private void mergeBuffer(ByteBuffer now) {
40         if (_receivedPacketBuf is null) {
41             version(HUNT_MESSAGE_DEBUG) tracef("buffering data: %d, bytes", now.remaining());
42             // ByteBuffer ret = newBuffer(now.remaining());
43             // ret.put(now).flip();
44             // _receivedPacketBuf = ret;
45             _receivedPacketBuf = now;
46         } else {
47             if (_receivedPacketBuf.hasRemaining()) {
48                 version(HUNT_MESSAGE_DEBUG) {
49                     tracef("merge buffer -> %s, %s", _receivedPacketBuf.remaining(), now.remaining());
50                 }
51                 ByteBuffer ret = newBuffer(_receivedPacketBuf.remaining() + now.remaining());
52                 ret.put(_receivedPacketBuf).put(now).flip();
53                 _receivedPacketBuf = ret;
54             } else {
55                 version(HUNT_MESSAGE_DEBUG) {
56                     tracef("buffering data: %s, bytes, current buffer: %s",
57                         now.toString(), _receivedPacketBuf.toString());
58                 }
59 
60                 if(now.remaining() <= _receivedPacketBuf.remaining()) {
61                     _receivedPacketBuf.clear();
62                     _receivedPacketBuf.put(now).flip();
63                 } else {
64                     ByteBuffer ret = newBuffer(now.remaining());
65                     ret.put(now).flip();
66                     _receivedPacketBuf = ret;
67                 }
68             }
69         }
70         version(HUNT_MESSAGE_DEBUG) trace(_receivedPacketBuf.toString());
71     }
72 
73     protected ByteBuffer newBuffer(int size) {
74         return BufferUtils.allocate(size);
75     }
76 
77     MessageBuffer[] parse(ByteBuffer buffer) {
78         // TODO: Tasks pending completion -@zhangxueping at 2019-11-13T10:07:54+08:00
79         // To handle big size frame
80 
81         version(HUNT_DEBUG) tracef("incoming buffer: %s", buffer);
82         mergeBuffer(buffer);
83 
84         MessageBuffer[] resultBuffers;
85         size_t dataStart = 0;
86 
87         while (_receivedPacketBuf.remaining() >= PACKET_HEADER_LENGTH) {
88             ubyte[] data = cast(ubyte[])_receivedPacketBuf.getRemaining();
89             //infof("rev buff: %s ----- %d",data, data.length);
90             PacketHeader header = PacketHeader.parse(data);
91             if(header is null) {
92                 warning("corrupted data");
93                 version(HUNT_DEBUG) {
94                     if(data.length<=64)
95                         infof("%(%02X %)", data[0 .. $]);
96                     else
97                         infof("%(%02X %) ...", data[0 .. 64]);
98                 }
99                 _receivedPacketBuf.clear().flip(); // All buffered data will be dropped.
100                 version(HUNT_MESSAGE_DEBUG) {
101                     tracef("_receivedPacketBuf: %d, %s",
102                      _receivedPacketBuf.remaining(), _receivedPacketBuf.toString());
103                 }
104                 return null;
105 
106             } else if(AvaliableMessageIds.length>0 && !AvaliableMessageIds.canFind(header.messageId())) {
107                 warningf("Unrecognized packet: %s", header.toString());
108                 _receivedPacketBuf.clear().flip();
109                 return null;
110             }
111 
112             version(HUNT_DEBUG) infof("packet header, %s", header.toString());
113 
114             size_t currentFrameSize = header.messageLength + header.extendLength() + PACKET_HEADER_LENGTH;
115             if (data.length < currentFrameSize) {
116                 // No enough data for a full frame, so save the remaining
117                 break;
118             }
119             //else if(data.length > MAX_PACKET_SIZE) {
120             //    warningf("Out of packet size (<= %d): %d", MAX_PACKET_SIZE, currentFrameSize);
121             //    return null;
122             //}
123             if (header.extendLength() > 0)
124             {
125                 //if(header.extendLength() == uint.sizeof)
126                 //{
127                   ubyte[Extend.sizeof] d = data[PACKET_HEADER_LENGTH .. PACKET_HEADER_LENGTH + cast(int)(header.extendLength())];
128                   //ubyte[Extend.sizeof] d
129                   //uint tagId = bigEndianToNative!uint(d);
130                   Extend extend  = cast(Extend)d;
131                   logInfof("Extend : %s ---  %s -----%s" , header.extendLength(),extend.tagId, extend.userId);
132                   resultBuffers ~= new MessageBuffer(header.messageId(), data[PACKET_HEADER_LENGTH + header.extendLength() ..currentFrameSize] , extend);
133                 //}
134                 //else
135                 //{
136                 //  ubyte[EXTENSION_FIELD_LENGTH] d1 = data[PACKET_HEADER_LENGTH .. PACKET_HEADER_LENGTH + uint.sizeof];
137                 //  uint tagId = bigEndianToNative!uint(d1);
138                 //  ubyte[EXTENSION_FIELD_LENGTH] d2 = data[PACKET_HEADER_LENGTH + uint.sizeof .. PACKET_HEADER_LENGTH + uint.sizeof + uint.sizeof];
139                 //  uint clientId = bigEndianToNative!uint(d2);
140                 //  logInfof("tagId : %s" , tagId);
141                 //  logInfof("clientId : %s" , clientId);
142                 //  resultBuffers ~= new MessageBuffer(header.messageId(), data[PACKET_HEADER_LENGTH + header.extendLength() ..currentFrameSize] , tagId ,clientId);
143                 //}
144             }else
145             {
146                 resultBuffers ~= new MessageBuffer(header.messageId(), data[PACKET_HEADER_LENGTH..currentFrameSize]);
147             }
148 
149             version(HUNT_MESSAGE_DEBUG) trace(_receivedPacketBuf.toString());
150             _receivedPacketBuf.nextGetIndex(cast(int)currentFrameSize);
151             version(HUNT_MESSAGE_DEBUG) trace(_receivedPacketBuf.toString());
152         }
153 
154         int remaining = _receivedPacketBuf.remaining();
155         version(HUNT_MESSAGE_DEBUG) {
156             tracef("remaining: %d, buffer: %s", remaining, _receivedPacketBuf.toString());
157         }
158 
159         if(remaining > 0) {
160             byte[] data = cast(byte[])_receivedPacketBuf.getRemaining();
161             size_t newLength = max(remaining, _defaultBufferSize);
162             if(_receivedPacketBuf is buffer || newLength > _receivedPacketBuf.capacity()) {
163                 version(HUNT_DEBUG) infof("reset buffer's size to %d bytes", newLength);
164                 _receivedPacketBuf = BufferUtils.allocate(cast(int)newLength);
165                 _receivedPacketBuf.put(data.dup).flip(); // buffer the remaining
166                 version(HUNT_MESSAGE_DEBUG) trace(_receivedPacketBuf.toString());
167             } else if(resultBuffers.length > 0) {
168                 version(HUNT_MESSAGE_DEBUG) warning("buffer the remaining: %s", _receivedPacketBuf.toString());
169                 _receivedPacketBuf.rewind();
170                 _receivedPacketBuf.put(data.dup).flip(); // buffer the remaining
171                 version(HUNT_MESSAGE_DEBUG) trace(_receivedPacketBuf.toString());
172             } else {
173                 version(HUNT_MESSAGE_DEBUG) warning("do nothing");
174             }
175         } else {
176             _receivedPacketBuf = null;
177         }
178 
179         return resultBuffers;
180     }
181 }