1 // Licensed to the Apache Software Foundation(ASF) under one
2 // or more contributor license agreements.See the NOTICE file
3 // distributed with this work for additional information
4 // regarding copyright ownership.The ASF licenses this file
5 // to you under the Apache License, Version 2.0 (the
6 // "License"); you may not use this file except in compliance
7 // with the License. You may obtain a copy of the License at
8 //
9 //     http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing,
12 // software distributed under the License is distributed on an
13 // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14 // KIND, either express or implied. See the License for the
15 // specific language governing permissions and limitations
16 // under the License.
17 
18 using System;
19 using System.Collections.Generic;
20 using System.Globalization;
21 using System.IO;
22 using System.Linq;
23 using System.Text;
24 using System.Threading;
25 using System.Threading.Tasks;
26 using Thrift.Protocol.Entities;
27 using Thrift.Protocol.Utilities;
28 using Thrift.Transport;
29 
30 
31 namespace Thrift.Protocol
32 {
33     /// <summary>
34     ///     JSON protocol implementation for thrift.
35     ///     This is a full-featured protocol supporting Write and Read.
36     ///     Please see the C++ class header for a detailed description of the
37     ///     protocol's wire format.
38     ///     Adapted from the Java version.
39     /// </summary>
40     // ReSharper disable once InconsistentNaming
41     public class TJsonProtocol : TProtocol
42     {
43         private const long Version = 1;
44 
45         // Temporary buffer used by several methods
46         private readonly byte[] _tempBuffer = new byte[4];
47 
48         // Current context that we are in
49         protected JSONBaseContext Context;
50 
51         // Stack of nested contexts that we may be in
52         protected Stack<JSONBaseContext> ContextStack = new Stack<JSONBaseContext>();
53 
54         // Reader that manages a 1-byte buffer
55         protected LookaheadReader Reader;
56 
57         // Default encoding
58         protected Encoding Utf8Encoding = Encoding.UTF8;
59 
60         /// <summary>
61         ///     TJsonProtocol Constructor
62         /// </summary>
TJsonProtocol(TTransport trans)63         public TJsonProtocol(TTransport trans)
64             : base(trans)
65         {
66             Context = new JSONBaseContext(this);
67             Reader = new LookaheadReader(this);
68         }
69 
70         /// <summary>
71         ///     Push a new JSON context onto the stack.
72         /// </summary>
PushContext(JSONBaseContext c)73         protected void PushContext(JSONBaseContext c)
74         {
75             ContextStack.Push(Context);
76             Context = c;
77         }
78 
79         /// <summary>
80         ///     Pop the last JSON context off the stack
81         /// </summary>
PopContext()82         protected void PopContext()
83         {
84             Context = ContextStack.Pop();
85         }
86 
87         /// <summary>
88         ///    Resets the context stack to pristine state. Allows for reusal of the protocol
89         ///    even in cases where the protocol instance was in an undefined state due to
90         ///    dangling/stale/obsolete contexts
91         /// </summary>
ResetContext()92         private void ResetContext()
93         {
94             ContextStack.Clear();
95             Context = new JSONBaseContext(this);
96         }
97         /// <summary>
98         ///     Read a byte that must match b[0]; otherwise an exception is thrown.
99         ///     Marked protected to avoid synthetic accessor in JSONListContext.Read
100         ///     and JSONPairContext.Read
101         /// </summary>
ReadJsonSyntaxCharAsync(byte[] bytes, CancellationToken cancellationToken)102         protected async Task ReadJsonSyntaxCharAsync(byte[] bytes, CancellationToken cancellationToken)
103         {
104             var ch = await Reader.ReadAsync(cancellationToken);
105             if (ch != bytes[0])
106             {
107                 throw new TProtocolException(TProtocolException.INVALID_DATA, $"Unexpected character: {(char) ch}");
108             }
109         }
110 
111         /// <summary>
112         ///     Write the bytes in array buf as a JSON characters, escaping as needed
113         /// </summary>
WriteJsonStringAsync(byte[] bytes, CancellationToken cancellationToken)114         private async Task WriteJsonStringAsync(byte[] bytes, CancellationToken cancellationToken)
115         {
116             await Context.WriteConditionalDelimiterAsync(cancellationToken);
117             await Trans.WriteAsync(TJSONProtocolConstants.Quote, cancellationToken);
118 
119             var len = bytes.Length;
120             for (var i = 0; i < len; i++)
121             {
122                 if ((bytes[i] & 0x00FF) >= 0x30)
123                 {
124                     if (bytes[i] == TJSONProtocolConstants.Backslash[0])
125                     {
126                         await Trans.WriteAsync(TJSONProtocolConstants.Backslash, cancellationToken);
127                         await Trans.WriteAsync(TJSONProtocolConstants.Backslash, cancellationToken);
128                     }
129                     else
130                     {
131                         await Trans.WriteAsync(bytes, i, 1, cancellationToken);
132                     }
133                 }
134                 else
135                 {
136                     _tempBuffer[0] = TJSONProtocolConstants.JsonCharTable[bytes[i]];
137                     if (_tempBuffer[0] == 1)
138                     {
139                         await Trans.WriteAsync(bytes, i, 1, cancellationToken);
140                     }
141                     else if (_tempBuffer[0] > 1)
142                     {
143                         await Trans.WriteAsync(TJSONProtocolConstants.Backslash, cancellationToken);
144                         await Trans.WriteAsync(_tempBuffer, 0, 1, cancellationToken);
145                     }
146                     else
147                     {
148                         await Trans.WriteAsync(TJSONProtocolConstants.EscSequences, cancellationToken);
149                         _tempBuffer[0] = TJSONProtocolHelper.ToHexChar((byte) (bytes[i] >> 4));
150                         _tempBuffer[1] = TJSONProtocolHelper.ToHexChar(bytes[i]);
151                         await Trans.WriteAsync(_tempBuffer, 0, 2, cancellationToken);
152                     }
153                 }
154             }
155             await Trans.WriteAsync(TJSONProtocolConstants.Quote, cancellationToken);
156         }
157 
158         /// <summary>
159         ///     Write out number as a JSON value. If the context dictates so, it will be
160         ///     wrapped in quotes to output as a JSON string.
161         /// </summary>
WriteJsonIntegerAsync(long num, CancellationToken cancellationToken)162         private async Task WriteJsonIntegerAsync(long num, CancellationToken cancellationToken)
163         {
164             await Context.WriteConditionalDelimiterAsync(cancellationToken);
165             var str = num.ToString();
166 
167             var escapeNum = Context.EscapeNumbers();
168             if (escapeNum)
169             {
170                 await Trans.WriteAsync(TJSONProtocolConstants.Quote, cancellationToken);
171             }
172 
173             var bytes = Utf8Encoding.GetBytes(str);
174             await Trans.WriteAsync(bytes, cancellationToken);
175 
176             if (escapeNum)
177             {
178                 await Trans.WriteAsync(TJSONProtocolConstants.Quote, cancellationToken);
179             }
180         }
181 
182         /// <summary>
183         ///     Write out a double as a JSON value. If it is NaN or infinity or if the
184         ///     context dictates escaping, Write out as JSON string.
185         /// </summary>
WriteJsonDoubleAsync(double num, CancellationToken cancellationToken)186         private async Task WriteJsonDoubleAsync(double num, CancellationToken cancellationToken)
187         {
188             await Context.WriteConditionalDelimiterAsync(cancellationToken);
189             var str = num.ToString("G17", CultureInfo.InvariantCulture);
190             var special = false;
191 
192             switch (str[0])
193             {
194                 case 'N': // NaN
195                 case 'I': // Infinity
196                     special = true;
197                     break;
198                 case '-':
199                     if (str[1] == 'I')
200                     {
201                         // -Infinity
202                         special = true;
203                     }
204                     break;
205             }
206 
207             var escapeNum = special || Context.EscapeNumbers();
208 
209             if (escapeNum)
210             {
211                 await Trans.WriteAsync(TJSONProtocolConstants.Quote, cancellationToken);
212             }
213 
214             await Trans.WriteAsync(Utf8Encoding.GetBytes(str), cancellationToken);
215 
216             if (escapeNum)
217             {
218                 await Trans.WriteAsync(TJSONProtocolConstants.Quote, cancellationToken);
219             }
220         }
221 
222         /// <summary>
223         ///     Write out contents of byte array b as a JSON string with base-64 encoded
224         ///     data
225         /// </summary>
WriteJsonBase64Async(byte[] bytes, CancellationToken cancellationToken)226         private async Task WriteJsonBase64Async(byte[] bytes, CancellationToken cancellationToken)
227         {
228             await Context.WriteConditionalDelimiterAsync(cancellationToken);
229             await Trans.WriteAsync(TJSONProtocolConstants.Quote, cancellationToken);
230 
231             var len = bytes.Length;
232             var off = 0;
233 
234             while (len >= 3)
235             {
236                 // Encode 3 bytes at a time
237                 TBase64Utils.Encode(bytes, off, 3, _tempBuffer, 0);
238                 await Trans.WriteAsync(_tempBuffer, 0, 4, cancellationToken);
239                 off += 3;
240                 len -= 3;
241             }
242 
243             if (len > 0)
244             {
245                 // Encode remainder
246                 TBase64Utils.Encode(bytes, off, len, _tempBuffer, 0);
247                 await Trans.WriteAsync(_tempBuffer, 0, len + 1, cancellationToken);
248             }
249 
250             await Trans.WriteAsync(TJSONProtocolConstants.Quote, cancellationToken);
251         }
252 
WriteJsonObjectStartAsync(CancellationToken cancellationToken)253         private async Task WriteJsonObjectStartAsync(CancellationToken cancellationToken)
254         {
255             await Context.WriteConditionalDelimiterAsync(cancellationToken);
256             await Trans.WriteAsync(TJSONProtocolConstants.LeftBrace, cancellationToken);
257             PushContext(new JSONPairContext(this));
258         }
259 
WriteJsonObjectEndAsync(CancellationToken cancellationToken)260         private async Task WriteJsonObjectEndAsync(CancellationToken cancellationToken)
261         {
262             PopContext();
263             await Trans.WriteAsync(TJSONProtocolConstants.RightBrace, cancellationToken);
264         }
265 
WriteJsonArrayStartAsync(CancellationToken cancellationToken)266         private async Task WriteJsonArrayStartAsync(CancellationToken cancellationToken)
267         {
268             await Context.WriteConditionalDelimiterAsync(cancellationToken);
269             await Trans.WriteAsync(TJSONProtocolConstants.LeftBracket, cancellationToken);
270             PushContext(new JSONListContext(this));
271         }
272 
WriteJsonArrayEndAsync(CancellationToken cancellationToken)273         private async Task WriteJsonArrayEndAsync(CancellationToken cancellationToken)
274         {
275             PopContext();
276             await Trans.WriteAsync(TJSONProtocolConstants.RightBracket, cancellationToken);
277         }
278 
WriteMessageBeginAsync(TMessage message, CancellationToken cancellationToken)279         public override async Task WriteMessageBeginAsync(TMessage message, CancellationToken cancellationToken)
280         {
281             ResetContext();
282             await WriteJsonArrayStartAsync(cancellationToken);
283             await WriteJsonIntegerAsync(Version, cancellationToken);
284 
285             var b = Utf8Encoding.GetBytes(message.Name);
286             await WriteJsonStringAsync(b, cancellationToken);
287 
288             await WriteJsonIntegerAsync((long) message.Type, cancellationToken);
289             await WriteJsonIntegerAsync(message.SeqID, cancellationToken);
290         }
291 
WriteMessageEndAsync(CancellationToken cancellationToken)292         public override async Task WriteMessageEndAsync(CancellationToken cancellationToken)
293         {
294             await WriteJsonArrayEndAsync(cancellationToken);
295         }
296 
297         public override async Task WriteStructBeginAsync(TStruct @struct, CancellationToken cancellationToken)
298         {
299             await WriteJsonObjectStartAsync(cancellationToken);
300         }
301 
WriteStructEndAsync(CancellationToken cancellationToken)302         public override async Task WriteStructEndAsync(CancellationToken cancellationToken)
303         {
304             await WriteJsonObjectEndAsync(cancellationToken);
305         }
306 
WriteFieldBeginAsync(TField field, CancellationToken cancellationToken)307         public override async Task WriteFieldBeginAsync(TField field, CancellationToken cancellationToken)
308         {
309             await WriteJsonIntegerAsync(field.ID, cancellationToken);
310             await WriteJsonObjectStartAsync(cancellationToken);
311             await WriteJsonStringAsync(TJSONProtocolHelper.GetTypeNameForTypeId(field.Type), cancellationToken);
312         }
313 
WriteFieldEndAsync(CancellationToken cancellationToken)314         public override async Task WriteFieldEndAsync(CancellationToken cancellationToken)
315         {
316             await WriteJsonObjectEndAsync(cancellationToken);
317         }
318 
WriteFieldStopAsync(CancellationToken cancellationToken)319         public override Task WriteFieldStopAsync(CancellationToken cancellationToken)
320         {
321             cancellationToken.ThrowIfCancellationRequested();
322             return Task.CompletedTask;
323         }
324 
WriteMapBeginAsync(TMap map, CancellationToken cancellationToken)325         public override async Task WriteMapBeginAsync(TMap map, CancellationToken cancellationToken)
326         {
327             await WriteJsonArrayStartAsync(cancellationToken);
328             await WriteJsonStringAsync(TJSONProtocolHelper.GetTypeNameForTypeId(map.KeyType), cancellationToken);
329             await WriteJsonStringAsync(TJSONProtocolHelper.GetTypeNameForTypeId(map.ValueType), cancellationToken);
330             await WriteJsonIntegerAsync(map.Count, cancellationToken);
331             await WriteJsonObjectStartAsync(cancellationToken);
332         }
333 
WriteMapEndAsync(CancellationToken cancellationToken)334         public override async Task WriteMapEndAsync(CancellationToken cancellationToken)
335         {
336             await WriteJsonObjectEndAsync(cancellationToken);
337             await WriteJsonArrayEndAsync(cancellationToken);
338         }
339 
WriteListBeginAsync(TList list, CancellationToken cancellationToken)340         public override async Task WriteListBeginAsync(TList list, CancellationToken cancellationToken)
341         {
342             await WriteJsonArrayStartAsync(cancellationToken);
343             await WriteJsonStringAsync(TJSONProtocolHelper.GetTypeNameForTypeId(list.ElementType), cancellationToken);
344             await WriteJsonIntegerAsync(list.Count, cancellationToken);
345         }
346 
WriteListEndAsync(CancellationToken cancellationToken)347         public override async Task WriteListEndAsync(CancellationToken cancellationToken)
348         {
349             await WriteJsonArrayEndAsync(cancellationToken);
350         }
351 
WriteSetBeginAsync(TSet set, CancellationToken cancellationToken)352         public override async Task WriteSetBeginAsync(TSet set, CancellationToken cancellationToken)
353         {
354             await WriteJsonArrayStartAsync(cancellationToken);
355             await WriteJsonStringAsync(TJSONProtocolHelper.GetTypeNameForTypeId(set.ElementType), cancellationToken);
356             await WriteJsonIntegerAsync(set.Count, cancellationToken);
357         }
358 
WriteSetEndAsync(CancellationToken cancellationToken)359         public override async Task WriteSetEndAsync(CancellationToken cancellationToken)
360         {
361             await WriteJsonArrayEndAsync(cancellationToken);
362         }
363 
WriteBoolAsync(bool b, CancellationToken cancellationToken)364         public override async Task WriteBoolAsync(bool b, CancellationToken cancellationToken)
365         {
366             await WriteJsonIntegerAsync(b ? 1 : 0, cancellationToken);
367         }
368 
WriteByteAsync(sbyte b, CancellationToken cancellationToken)369         public override async Task WriteByteAsync(sbyte b, CancellationToken cancellationToken)
370         {
371             await WriteJsonIntegerAsync(b, cancellationToken);
372         }
373 
WriteI16Async(short i16, CancellationToken cancellationToken)374         public override async Task WriteI16Async(short i16, CancellationToken cancellationToken)
375         {
376             await WriteJsonIntegerAsync(i16, cancellationToken);
377         }
378 
WriteI32Async(int i32, CancellationToken cancellationToken)379         public override async Task WriteI32Async(int i32, CancellationToken cancellationToken)
380         {
381             await WriteJsonIntegerAsync(i32, cancellationToken);
382         }
383 
WriteI64Async(long i64, CancellationToken cancellationToken)384         public override async Task WriteI64Async(long i64, CancellationToken cancellationToken)
385         {
386             await WriteJsonIntegerAsync(i64, cancellationToken);
387         }
388 
WriteDoubleAsync(double d, CancellationToken cancellationToken)389         public override async Task WriteDoubleAsync(double d, CancellationToken cancellationToken)
390         {
391             await WriteJsonDoubleAsync(d, cancellationToken);
392         }
393 
WriteStringAsync(string s, CancellationToken cancellationToken)394         public override async Task WriteStringAsync(string s, CancellationToken cancellationToken)
395         {
396             var b = Utf8Encoding.GetBytes(s);
397             await WriteJsonStringAsync(b, cancellationToken);
398         }
399 
WriteBinaryAsync(byte[] bytes, CancellationToken cancellationToken)400         public override async Task WriteBinaryAsync(byte[] bytes, CancellationToken cancellationToken)
401         {
402             await WriteJsonBase64Async(bytes, cancellationToken);
403         }
WriteUuidAsync(Guid uuid, CancellationToken cancellationToken = default)404         public override async Task WriteUuidAsync(Guid uuid, CancellationToken cancellationToken = default)
405         {
406             await WriteStringAsync(uuid.ToString("D"), cancellationToken);  // no curly braces
407         }
408 
409         /// <summary>
410         ///     Read in a JSON string, unescaping as appropriate.. Skip Reading from the
411         ///     context if skipContext is true.
412         /// </summary>
ReadJsonStringAsync(bool skipContext, CancellationToken cancellationToken)413         private async ValueTask<byte[]> ReadJsonStringAsync(bool skipContext, CancellationToken cancellationToken)
414         {
415             using (var buffer = new MemoryStream())
416             {
417                 var codeunits = new List<char>();
418 
419 
420                 if (!skipContext)
421                 {
422                     await Context.ReadConditionalDelimiterAsync(cancellationToken);
423                 }
424 
425                 await ReadJsonSyntaxCharAsync(TJSONProtocolConstants.Quote, cancellationToken);
426 
427                 while (true)
428                 {
429                     var ch = await Reader.ReadAsync(cancellationToken);
430                     if (ch == TJSONProtocolConstants.Quote[0])
431                     {
432                         break;
433                     }
434 
435                     // escaped?
436                     if (ch != TJSONProtocolConstants.EscSequences[0])
437                     {
438 #if NETSTANDARD2_0
439                         await buffer.WriteAsync(new[] {ch}, 0, 1, cancellationToken);
440 #else
441                         var wbuf = new[] { ch };
442                         await buffer.WriteAsync(wbuf.AsMemory(0, 1), cancellationToken);
443 #endif
444                         continue;
445                     }
446 
447                     // distinguish between \uXXXX and \?
448                     ch = await Reader.ReadAsync(cancellationToken);
449                     if (ch != TJSONProtocolConstants.EscSequences[1]) // control chars like \n
450                     {
451                         var off = Array.IndexOf(TJSONProtocolConstants.EscapeChars, (char) ch);
452                         if (off == -1)
453                         {
454                             throw new TProtocolException(TProtocolException.INVALID_DATA, "Expected control char");
455                         }
456                         ch = TJSONProtocolConstants.EscapeCharValues[off];
457 #if NETSTANDARD2_0
458                         await buffer.WriteAsync(new[] {ch}, 0, 1, cancellationToken);
459 #else
460                         var wbuf = new[] { ch };
461                         await buffer.WriteAsync( wbuf.AsMemory(0, 1), cancellationToken);
462 #endif
463                         continue;
464                     }
465 
466                     // it's \uXXXX
467                     await Trans.ReadAllAsync(_tempBuffer, 0, 4, cancellationToken);
468 
469                     var wch = (short) ((TJSONProtocolHelper.ToHexVal(_tempBuffer[0]) << 12) +
470                                        (TJSONProtocolHelper.ToHexVal(_tempBuffer[1]) << 8) +
471                                        (TJSONProtocolHelper.ToHexVal(_tempBuffer[2]) << 4) +
472                                        TJSONProtocolHelper.ToHexVal(_tempBuffer[3]));
473 
474                     if (char.IsHighSurrogate((char) wch))
475                     {
476                         if (codeunits.Count > 0)
477                         {
478                             throw new TProtocolException(TProtocolException.INVALID_DATA, "Expected low surrogate char");
479                         }
480                         codeunits.Add((char) wch);
481                     }
482                     else if (char.IsLowSurrogate((char) wch))
483                     {
484                         if (codeunits.Count == 0)
485                         {
486                             throw new TProtocolException(TProtocolException.INVALID_DATA, "Expected high surrogate char");
487                         }
488 
489                         codeunits.Add((char) wch);
490                         var tmp = Utf8Encoding.GetBytes(codeunits.ToArray());
491 #if NETSTANDARD2_0
492                         await buffer.WriteAsync(tmp, 0, tmp.Length, cancellationToken);
493 #else
494                         await buffer.WriteAsync(tmp.AsMemory(0, tmp.Length), cancellationToken);
495 #endif
496                         codeunits.Clear();
497                     }
498                     else
499                     {
500                         var tmp = Utf8Encoding.GetBytes(new[] { (char)wch });
501 #if NETSTANDARD2_0
502                         await buffer.WriteAsync(tmp, 0, tmp.Length, cancellationToken);
503 #else
504                         await buffer.WriteAsync(tmp.AsMemory( 0, tmp.Length), cancellationToken);
505 #endif
506                     }
507                 }
508 
509                 if (codeunits.Count > 0)
510                 {
511                     throw new TProtocolException(TProtocolException.INVALID_DATA, "Expected low surrogate char");
512                 }
513 
514                 return buffer.ToArray();
515             }
516         }
517 
518         /// <summary>
519         ///     Read in a sequence of characters that are all valid in JSON numbers. Does
520         ///     not do a complete regex check to validate that this is actually a number.
521         /// </summary>
ReadJsonNumericCharsAsync(CancellationToken cancellationToken)522         private async ValueTask<string> ReadJsonNumericCharsAsync(CancellationToken cancellationToken)
523         {
524             var strbld = new StringBuilder();
525             while (true)
526             {
527                 //TODO: workaround for primitive types with TJsonProtocol, think - how to rewrite into more easy form without exceptions
528                 try
529                 {
530                     var ch = await Reader.PeekAsync(cancellationToken);
531                     if (!TJSONProtocolHelper.IsJsonNumeric(ch))
532                     {
533                         break;
534                     }
535                     var c = (char)await Reader.ReadAsync(cancellationToken);
536                     strbld.Append(c);
537                 }
538                 catch (TTransportException)
539                 {
540                     break;
541                 }
542             }
543             return strbld.ToString();
544         }
545 
546         /// <summary>
547         ///     Read in a JSON number. If the context dictates, Read in enclosing quotes.
548         /// </summary>
ReadJsonIntegerAsync(CancellationToken cancellationToken)549         private async ValueTask<long> ReadJsonIntegerAsync(CancellationToken cancellationToken)
550         {
551             await Context.ReadConditionalDelimiterAsync(cancellationToken);
552             if (Context.EscapeNumbers())
553             {
554                 await ReadJsonSyntaxCharAsync(TJSONProtocolConstants.Quote, cancellationToken);
555             }
556 
557             var str = await ReadJsonNumericCharsAsync(cancellationToken);
558             if (Context.EscapeNumbers())
559             {
560                 await ReadJsonSyntaxCharAsync(TJSONProtocolConstants.Quote, cancellationToken);
561             }
562 
563             try
564             {
565                 return long.Parse(str);
566             }
567             catch (FormatException)
568             {
569                 throw new TProtocolException(TProtocolException.INVALID_DATA, "Bad data encounted in numeric data");
570             }
571         }
572 
573         /// <summary>
574         ///     Read in a JSON double value. Throw if the value is not wrapped in quotes
575         ///     when expected or if wrapped in quotes when not expected.
576         /// </summary>
ReadJsonDoubleAsync(CancellationToken cancellationToken)577         private async ValueTask<double> ReadJsonDoubleAsync(CancellationToken cancellationToken)
578         {
579             await Context.ReadConditionalDelimiterAsync(cancellationToken);
580             if (await Reader.PeekAsync(cancellationToken) == TJSONProtocolConstants.Quote[0])
581             {
582                 var arr = await ReadJsonStringAsync(true, cancellationToken);
583                 var dub = double.Parse(Utf8Encoding.GetString(arr, 0, arr.Length), CultureInfo.InvariantCulture);
584 
585                 if (!Context.EscapeNumbers() && !double.IsNaN(dub) && !double.IsInfinity(dub))
586                 {
587                     // Throw exception -- we should not be in a string in this case
588                     throw new TProtocolException(TProtocolException.INVALID_DATA, "Numeric data unexpectedly quoted");
589                 }
590 
591                 return dub;
592             }
593 
594             if (Context.EscapeNumbers())
595             {
596                 // This will throw - we should have had a quote if escapeNum == true
597                 await ReadJsonSyntaxCharAsync(TJSONProtocolConstants.Quote, cancellationToken);
598             }
599 
600             try
601             {
602                 return double.Parse(await ReadJsonNumericCharsAsync(cancellationToken), CultureInfo.InvariantCulture);
603             }
604             catch (FormatException)
605             {
606                 throw new TProtocolException(TProtocolException.INVALID_DATA, "Bad data encounted in numeric data");
607             }
608         }
609 
610         /// <summary>
611         ///     Read in a JSON string containing base-64 encoded data and decode it.
612         /// </summary>
ReadJsonBase64Async(CancellationToken cancellationToken)613         private async ValueTask<byte[]> ReadJsonBase64Async(CancellationToken cancellationToken)
614         {
615             var b = await ReadJsonStringAsync(false, cancellationToken);
616             var len = b.Length;
617             var off = 0;
618             var size = 0;
619 
620             // reduce len to ignore fill bytes
621             while ((len > 0) && (b[len - 1] == '='))
622             {
623                 --len;
624             }
625 
626             // read & decode full byte triplets = 4 source bytes
627             while (len > 4)
628             {
629                 // Decode 4 bytes at a time
630                 TBase64Utils.Decode(b, off, 4, b, size); // NB: decoded in place
631                 off += 4;
632                 len -= 4;
633                 size += 3;
634             }
635 
636             // Don't decode if we hit the end or got a single leftover byte (invalid
637             // base64 but legal for skip of regular string exType)
638             if (len > 1)
639             {
640                 // Decode remainder
641                 TBase64Utils.Decode(b, off, len, b, size); // NB: decoded in place
642                 size += len - 1;
643             }
644 
645             // Sadly we must copy the byte[] (any way around this?)
646             var result = new byte[size];
647             Array.Copy(b, 0, result, 0, size);
648             return result;
649         }
650 
ReadJsonObjectStartAsync(CancellationToken cancellationToken)651         private async Task ReadJsonObjectStartAsync(CancellationToken cancellationToken)
652         {
653             await Context.ReadConditionalDelimiterAsync(cancellationToken);
654             await ReadJsonSyntaxCharAsync(TJSONProtocolConstants.LeftBrace, cancellationToken);
655             PushContext(new JSONPairContext(this));
656         }
657 
ReadJsonObjectEndAsync(CancellationToken cancellationToken)658         private async Task ReadJsonObjectEndAsync(CancellationToken cancellationToken)
659         {
660             await ReadJsonSyntaxCharAsync(TJSONProtocolConstants.RightBrace, cancellationToken);
661             PopContext();
662         }
663 
ReadJsonArrayStartAsync(CancellationToken cancellationToken)664         private async Task ReadJsonArrayStartAsync(CancellationToken cancellationToken)
665         {
666             await Context.ReadConditionalDelimiterAsync(cancellationToken);
667             await ReadJsonSyntaxCharAsync(TJSONProtocolConstants.LeftBracket, cancellationToken);
668             PushContext(new JSONListContext(this));
669         }
670 
ReadJsonArrayEndAsync(CancellationToken cancellationToken)671         private async Task ReadJsonArrayEndAsync(CancellationToken cancellationToken)
672         {
673             await ReadJsonSyntaxCharAsync(TJSONProtocolConstants.RightBracket, cancellationToken);
674             PopContext();
675         }
676 
ReadMessageBeginAsync(CancellationToken cancellationToken)677         public override async ValueTask<TMessage> ReadMessageBeginAsync(CancellationToken cancellationToken)
678         {
679             var message = new TMessage();
680 
681             ResetContext();
682             await ReadJsonArrayStartAsync(cancellationToken);
683             if (await ReadJsonIntegerAsync(cancellationToken) != Version)
684             {
685                 throw new TProtocolException(TProtocolException.BAD_VERSION, "Message contained bad version.");
686             }
687 
688             var buf = await ReadJsonStringAsync(false, cancellationToken);
689             message.Name = Utf8Encoding.GetString(buf, 0, buf.Length);
690             message.Type = (TMessageType) await ReadJsonIntegerAsync(cancellationToken);
691             message.SeqID = (int) await ReadJsonIntegerAsync(cancellationToken);
692             return message;
693         }
694 
ReadMessageEndAsync(CancellationToken cancellationToken)695         public override async Task ReadMessageEndAsync(CancellationToken cancellationToken)
696         {
697             cancellationToken.ThrowIfCancellationRequested();
698             await ReadJsonArrayEndAsync(cancellationToken);
699             Transport.ResetConsumedMessageSize();
700         }
701 
ReadStructBeginAsync(CancellationToken cancellationToken)702         public override async ValueTask<TStruct> ReadStructBeginAsync(CancellationToken cancellationToken)
703         {
704             await ReadJsonObjectStartAsync(cancellationToken);
705 
706             return AnonymousStruct;
707         }
708 
ReadStructEndAsync(CancellationToken cancellationToken)709         public override async Task ReadStructEndAsync(CancellationToken cancellationToken)
710         {
711             await ReadJsonObjectEndAsync(cancellationToken);
712         }
713 
ReadFieldBeginAsync(CancellationToken cancellationToken)714         public override async ValueTask<TField> ReadFieldBeginAsync(CancellationToken cancellationToken)
715         {
716             var ch = await Reader.PeekAsync(cancellationToken);
717             if (ch == TJSONProtocolConstants.RightBrace[0])
718             {
719                 return StopField;
720             }
721 
722             var field = new TField()
723             {
724                 ID = (short)await ReadJsonIntegerAsync(cancellationToken)
725             };
726 
727             await ReadJsonObjectStartAsync(cancellationToken);
728             field.Type = TJSONProtocolHelper.GetTypeIdForTypeName(await ReadJsonStringAsync(false, cancellationToken));
729             return field;
730         }
731 
ReadFieldEndAsync(CancellationToken cancellationToken)732         public override async Task ReadFieldEndAsync(CancellationToken cancellationToken)
733         {
734             await ReadJsonObjectEndAsync(cancellationToken);
735         }
736 
ReadMapBeginAsync(CancellationToken cancellationToken)737         public override async ValueTask<TMap> ReadMapBeginAsync(CancellationToken cancellationToken)
738         {
739             var map = new TMap();
740             await ReadJsonArrayStartAsync(cancellationToken);
741             map.KeyType = TJSONProtocolHelper.GetTypeIdForTypeName(await ReadJsonStringAsync(false, cancellationToken));
742             map.ValueType = TJSONProtocolHelper.GetTypeIdForTypeName(await ReadJsonStringAsync(false, cancellationToken));
743             map.Count = (int) await ReadJsonIntegerAsync(cancellationToken);
744             CheckReadBytesAvailable(map);
745             await ReadJsonObjectStartAsync(cancellationToken);
746             return map;
747         }
748 
ReadMapEndAsync(CancellationToken cancellationToken)749         public override async Task ReadMapEndAsync(CancellationToken cancellationToken)
750         {
751             await ReadJsonObjectEndAsync(cancellationToken);
752             await ReadJsonArrayEndAsync(cancellationToken);
753         }
754 
ReadListBeginAsync(CancellationToken cancellationToken)755         public override async ValueTask<TList> ReadListBeginAsync(CancellationToken cancellationToken)
756         {
757             var list = new TList();
758             await ReadJsonArrayStartAsync(cancellationToken);
759             list.ElementType = TJSONProtocolHelper.GetTypeIdForTypeName(await ReadJsonStringAsync(false, cancellationToken));
760             list.Count = (int) await ReadJsonIntegerAsync(cancellationToken);
761             CheckReadBytesAvailable(list);
762             return list;
763         }
764 
ReadListEndAsync(CancellationToken cancellationToken)765         public override async Task ReadListEndAsync(CancellationToken cancellationToken)
766         {
767             await ReadJsonArrayEndAsync(cancellationToken);
768         }
769 
ReadSetBeginAsync(CancellationToken cancellationToken)770         public override async ValueTask<TSet> ReadSetBeginAsync(CancellationToken cancellationToken)
771         {
772             var set = new TSet();
773             await ReadJsonArrayStartAsync(cancellationToken);
774             set.ElementType = TJSONProtocolHelper.GetTypeIdForTypeName(await ReadJsonStringAsync(false, cancellationToken));
775             set.Count = (int) await ReadJsonIntegerAsync(cancellationToken);
776             CheckReadBytesAvailable(set);
777             return set;
778         }
779 
ReadSetEndAsync(CancellationToken cancellationToken)780         public override async Task ReadSetEndAsync(CancellationToken cancellationToken)
781         {
782             await ReadJsonArrayEndAsync(cancellationToken);
783         }
784 
ReadBoolAsync(CancellationToken cancellationToken)785         public override async ValueTask<bool> ReadBoolAsync(CancellationToken cancellationToken)
786         {
787             return await ReadJsonIntegerAsync(cancellationToken) != 0;
788         }
789 
ReadByteAsync(CancellationToken cancellationToken)790         public override async ValueTask<sbyte> ReadByteAsync(CancellationToken cancellationToken)
791         {
792             return (sbyte) await ReadJsonIntegerAsync(cancellationToken);
793         }
794 
ReadI16Async(CancellationToken cancellationToken)795         public override async ValueTask<short> ReadI16Async(CancellationToken cancellationToken)
796         {
797             return (short) await ReadJsonIntegerAsync(cancellationToken);
798         }
799 
ReadI32Async(CancellationToken cancellationToken)800         public override async ValueTask<int> ReadI32Async(CancellationToken cancellationToken)
801         {
802             return (int) await ReadJsonIntegerAsync(cancellationToken);
803         }
804 
ReadI64Async(CancellationToken cancellationToken)805         public override async ValueTask<long> ReadI64Async(CancellationToken cancellationToken)
806         {
807             return await ReadJsonIntegerAsync(cancellationToken);
808         }
809 
ReadDoubleAsync(CancellationToken cancellationToken)810         public override async ValueTask<double> ReadDoubleAsync(CancellationToken cancellationToken)
811         {
812             return await ReadJsonDoubleAsync(cancellationToken);
813         }
814 
ReadStringAsync(CancellationToken cancellationToken)815         public override async ValueTask<string> ReadStringAsync(CancellationToken cancellationToken)
816         {
817             var buf = await ReadJsonStringAsync(false, cancellationToken);
818             return Utf8Encoding.GetString(buf, 0, buf.Length);
819         }
820 
ReadBinaryAsync(CancellationToken cancellationToken)821         public override async ValueTask<byte[]> ReadBinaryAsync(CancellationToken cancellationToken)
822         {
823             return await ReadJsonBase64Async(cancellationToken);
824         }
825 
ReadUuidAsync(CancellationToken cancellationToken = default)826         public override async ValueTask<Guid> ReadUuidAsync(CancellationToken cancellationToken = default)
827         {
828             return new Guid( await ReadStringAsync(cancellationToken));
829         }
830 
831         // Return the minimum number of bytes a type will consume on the wire
GetMinSerializedSize(TType type)832         public override int GetMinSerializedSize(TType type)
833         {
834             switch (type)
835             {
836                 case TType.Stop: return 0;
837                 case TType.Void: return 0;
838                 case TType.Bool: return 1;  // written as int
839                 case TType.Byte: return 1;
840                 case TType.Double: return 1;
841                 case TType.I16: return 1;
842                 case TType.I32: return 1;
843                 case TType.I64: return 1;
844                 case TType.String: return 2;  // empty string
845                 case TType.Struct: return 2;  // empty struct
846                 case TType.Map: return 2;  // empty map
847                 case TType.Set: return 2;  // empty set
848                 case TType.List: return 2;  // empty list
849                 case TType.Uuid: return 36;  // "E236974D-F0B0-4E05-8F29-0B455D41B1A1"
850                 default: throw new TProtocolException(TProtocolException.NOT_IMPLEMENTED, "unrecognized type code");
851             }
852         }
853 
854         /// <summary>
855         ///     Factory for JSON protocol objects
856         /// </summary>
857         public class Factory : TProtocolFactory
858         {
GetProtocol(TTransport trans)859             public override TProtocol GetProtocol(TTransport trans)
860             {
861                 return new TJsonProtocol(trans);
862             }
863         }
864 
865         /// <summary>
866         ///     Base class for tracking JSON contexts that may require
867         ///     inserting/Reading additional JSON syntax characters
868         ///     This base context does nothing.
869         /// </summary>
870         protected class JSONBaseContext
871         {
872             protected TJsonProtocol Proto;
873 
JSONBaseContext(TJsonProtocol proto)874             public JSONBaseContext(TJsonProtocol proto)
875             {
876                 Proto = proto;
877             }
878 
WriteConditionalDelimiterAsync(CancellationToken cancellationToken)879             public virtual Task WriteConditionalDelimiterAsync(CancellationToken cancellationToken)
880             {
881                 cancellationToken.ThrowIfCancellationRequested();
882                 return Task.CompletedTask;
883             }
884 
ReadConditionalDelimiterAsync(CancellationToken cancellationToken)885             public virtual Task ReadConditionalDelimiterAsync(CancellationToken cancellationToken)
886             {
887                 cancellationToken.ThrowIfCancellationRequested();
888                 return Task.CompletedTask;
889             }
890 
EscapeNumbers()891             public virtual bool EscapeNumbers()
892             {
893                 return false;
894             }
895         }
896 
897         /// <summary>
898         ///     Context for JSON lists. Will insert/Read commas before each item except
899         ///     for the first one
900         /// </summary>
901         protected class JSONListContext : JSONBaseContext
902         {
903             private bool _first = true;
904 
JSONListContext(TJsonProtocol protocol)905             public JSONListContext(TJsonProtocol protocol)
906                 : base(protocol)
907             {
908             }
909 
WriteConditionalDelimiterAsync(CancellationToken cancellationToken)910             public override async Task WriteConditionalDelimiterAsync(CancellationToken cancellationToken)
911             {
912                 if (_first)
913                 {
914                     _first = false;
915                 }
916                 else
917                 {
918                     await Proto.Trans.WriteAsync(TJSONProtocolConstants.Comma, cancellationToken);
919                 }
920             }
921 
ReadConditionalDelimiterAsync(CancellationToken cancellationToken)922             public override async Task ReadConditionalDelimiterAsync(CancellationToken cancellationToken)
923             {
924                 if (_first)
925                 {
926                     _first = false;
927                 }
928                 else
929                 {
930                     await Proto.ReadJsonSyntaxCharAsync(TJSONProtocolConstants.Comma, cancellationToken);
931                 }
932             }
933         }
934 
935         /// <summary>
936         ///     Context for JSON records. Will insert/Read colons before the value portion
937         ///     of each record pair, and commas before each key except the first. In
938         ///     addition, will indicate that numbers in the key position need to be
939         ///     escaped in quotes (since JSON keys must be strings).
940         /// </summary>
941         // ReSharper disable once InconsistentNaming
942         protected class JSONPairContext : JSONBaseContext
943         {
944             private bool _colon = true;
945 
946             private bool _first = true;
947 
JSONPairContext(TJsonProtocol proto)948             public JSONPairContext(TJsonProtocol proto)
949                 : base(proto)
950             {
951             }
952 
WriteConditionalDelimiterAsync(CancellationToken cancellationToken)953             public override async Task WriteConditionalDelimiterAsync(CancellationToken cancellationToken)
954             {
955                 if (_first)
956                 {
957                     _first = false;
958                     _colon = true;
959                 }
960                 else
961                 {
962                     await Proto.Trans.WriteAsync(_colon ? TJSONProtocolConstants.Colon : TJSONProtocolConstants.Comma, cancellationToken);
963                     _colon = !_colon;
964                 }
965             }
966 
ReadConditionalDelimiterAsync(CancellationToken cancellationToken)967             public override async Task ReadConditionalDelimiterAsync(CancellationToken cancellationToken)
968             {
969                 if (_first)
970                 {
971                     _first = false;
972                     _colon = true;
973                 }
974                 else
975                 {
976                     await Proto.ReadJsonSyntaxCharAsync(_colon ? TJSONProtocolConstants.Colon : TJSONProtocolConstants.Comma, cancellationToken);
977                     _colon = !_colon;
978                 }
979             }
980 
EscapeNumbers()981             public override bool EscapeNumbers()
982             {
983                 return _colon;
984             }
985         }
986 
987         /// <summary>
988         ///     Holds up to one byte from the transport
989         /// </summary>
990         protected class LookaheadReader
991         {
992             private readonly byte[] _data = new byte[1];
993 
994             private bool _hasData;
995             protected TJsonProtocol Proto;
996 
LookaheadReader(TJsonProtocol proto)997             public LookaheadReader(TJsonProtocol proto)
998             {
999                 Proto = proto;
1000             }
1001 
1002             /// <summary>
1003             ///     Return and consume the next byte to be Read, either taking it from the
1004             ///     data buffer if present or getting it from the transport otherwise.
1005             /// </summary>
ReadAsync(CancellationToken cancellationToken)1006             public async ValueTask<byte> ReadAsync(CancellationToken cancellationToken)
1007             {
1008                 cancellationToken.ThrowIfCancellationRequested();
1009 
1010                 if (_hasData)
1011                 {
1012                     _hasData = false;
1013                 }
1014                 else
1015                 {
1016                     // find more easy way to avoid exception on reading primitive types
1017                     await Proto.Trans.ReadAllAsync(_data, 0, 1, cancellationToken);
1018                 }
1019                 return _data[0];
1020             }
1021 
1022             /// <summary>
1023             ///     Return the next byte to be Read without consuming, filling the data
1024             ///     buffer if it has not been filled alReady.
1025             /// </summary>
PeekAsync(CancellationToken cancellationToken)1026             public async ValueTask<byte> PeekAsync(CancellationToken cancellationToken)
1027             {
1028                 cancellationToken.ThrowIfCancellationRequested();
1029 
1030                 if (!_hasData)
1031                 {
1032                     // find more easy way to avoid exception on reading primitive types
1033                     await Proto.Trans.ReadAllAsync(_data, 0, 1, cancellationToken);
1034                     _hasData = true;
1035                 }
1036                 return _data[0];
1037             }
1038         }
1039     }
1040 }
1041