blob: c100d86271f6804d93abb910ab2624a31b23a0cd [file] [log] [blame]
Jens Geyeraa0c8b32019-01-28 23:27:45 +01001// 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
18using System;
19using System.Collections.Generic;
20using System.Globalization;
21using System.IO;
22using System.Linq;
23using System.Text;
24using System.Threading;
25using System.Threading.Tasks;
26using Thrift.Protocol.Entities;
27using Thrift.Protocol.Utilities;
28using Thrift.Transport;
29
Jens Geyer0d128322021-02-25 09:42:52 +010030
Jens Geyeraa0c8b32019-01-28 23:27:45 +010031namespace 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>
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>
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>
82 protected void PopContext()
83 {
84 Context = ContextStack.Pop();
85 }
86
87 /// <summary>
Paulo Nevesf049ff32020-02-05 11:58:18 +010088 /// 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>
Jens Geyer0d128322021-02-25 09:42:52 +010092 private void ResetContext()
Paulo Nevesf049ff32020-02-05 11:58:18 +010093 {
94 ContextStack.Clear();
95 Context = new JSONBaseContext(this);
96 }
97 /// <summary>
Jens Geyeraa0c8b32019-01-28 23:27:45 +010098 /// 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>
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>
114 private async Task WriteJsonStringAsync(byte[] bytes, CancellationToken cancellationToken)
115 {
Jens Geyer2ff952b2019-04-13 19:46:54 +0200116 await Context.WriteConditionalDelimiterAsync(cancellationToken);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100117 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 {
Jens Geyerdce22992020-05-16 23:02:27 +0200131 await Trans.WriteAsync(bytes, i, 1, cancellationToken);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100132 }
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>
162 private async Task WriteJsonIntegerAsync(long num, CancellationToken cancellationToken)
163 {
Jens Geyer2ff952b2019-04-13 19:46:54 +0200164 await Context.WriteConditionalDelimiterAsync(cancellationToken);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100165 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>
186 private async Task WriteJsonDoubleAsync(double num, CancellationToken cancellationToken)
187 {
Jens Geyer2ff952b2019-04-13 19:46:54 +0200188 await Context.WriteConditionalDelimiterAsync(cancellationToken);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100189 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>
226 private async Task WriteJsonBase64Async(byte[] bytes, CancellationToken cancellationToken)
227 {
Jens Geyer2ff952b2019-04-13 19:46:54 +0200228 await Context.WriteConditionalDelimiterAsync(cancellationToken);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100229 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
253 private async Task WriteJsonObjectStartAsync(CancellationToken cancellationToken)
254 {
Jens Geyer2ff952b2019-04-13 19:46:54 +0200255 await Context.WriteConditionalDelimiterAsync(cancellationToken);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100256 await Trans.WriteAsync(TJSONProtocolConstants.LeftBrace, cancellationToken);
257 PushContext(new JSONPairContext(this));
258 }
259
260 private async Task WriteJsonObjectEndAsync(CancellationToken cancellationToken)
261 {
262 PopContext();
263 await Trans.WriteAsync(TJSONProtocolConstants.RightBrace, cancellationToken);
264 }
265
266 private async Task WriteJsonArrayStartAsync(CancellationToken cancellationToken)
267 {
Jens Geyer2ff952b2019-04-13 19:46:54 +0200268 await Context.WriteConditionalDelimiterAsync(cancellationToken);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100269 await Trans.WriteAsync(TJSONProtocolConstants.LeftBracket, cancellationToken);
270 PushContext(new JSONListContext(this));
271 }
272
273 private async Task WriteJsonArrayEndAsync(CancellationToken cancellationToken)
274 {
275 PopContext();
276 await Trans.WriteAsync(TJSONProtocolConstants.RightBracket, cancellationToken);
277 }
278
279 public override async Task WriteMessageBeginAsync(TMessage message, CancellationToken cancellationToken)
280 {
Jens Geyer0d128322021-02-25 09:42:52 +0100281 ResetContext();
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100282 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
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
302 public override async Task WriteStructEndAsync(CancellationToken cancellationToken)
303 {
304 await WriteJsonObjectEndAsync(cancellationToken);
305 }
306
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
314 public override async Task WriteFieldEndAsync(CancellationToken cancellationToken)
315 {
316 await WriteJsonObjectEndAsync(cancellationToken);
317 }
318
Jens Geyerdce22992020-05-16 23:02:27 +0200319 public override Task WriteFieldStopAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100320 {
Jens Geyerdce22992020-05-16 23:02:27 +0200321 cancellationToken.ThrowIfCancellationRequested();
322 return Task.CompletedTask;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100323 }
324
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
334 public override async Task WriteMapEndAsync(CancellationToken cancellationToken)
335 {
336 await WriteJsonObjectEndAsync(cancellationToken);
337 await WriteJsonArrayEndAsync(cancellationToken);
338 }
339
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
347 public override async Task WriteListEndAsync(CancellationToken cancellationToken)
348 {
349 await WriteJsonArrayEndAsync(cancellationToken);
350 }
351
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
359 public override async Task WriteSetEndAsync(CancellationToken cancellationToken)
360 {
361 await WriteJsonArrayEndAsync(cancellationToken);
362 }
363
364 public override async Task WriteBoolAsync(bool b, CancellationToken cancellationToken)
365 {
366 await WriteJsonIntegerAsync(b ? 1 : 0, cancellationToken);
367 }
368
369 public override async Task WriteByteAsync(sbyte b, CancellationToken cancellationToken)
370 {
371 await WriteJsonIntegerAsync(b, cancellationToken);
372 }
373
374 public override async Task WriteI16Async(short i16, CancellationToken cancellationToken)
375 {
376 await WriteJsonIntegerAsync(i16, cancellationToken);
377 }
378
379 public override async Task WriteI32Async(int i32, CancellationToken cancellationToken)
380 {
381 await WriteJsonIntegerAsync(i32, cancellationToken);
382 }
383
384 public override async Task WriteI64Async(long i64, CancellationToken cancellationToken)
385 {
386 await WriteJsonIntegerAsync(i64, cancellationToken);
387 }
388
389 public override async Task WriteDoubleAsync(double d, CancellationToken cancellationToken)
390 {
391 await WriteJsonDoubleAsync(d, cancellationToken);
392 }
393
394 public override async Task WriteStringAsync(string s, CancellationToken cancellationToken)
395 {
396 var b = Utf8Encoding.GetBytes(s);
397 await WriteJsonStringAsync(b, cancellationToken);
398 }
399
400 public override async Task WriteBinaryAsync(byte[] bytes, CancellationToken cancellationToken)
401 {
402 await WriteJsonBase64Async(bytes, cancellationToken);
403 }
Jens Geyer62445c12022-06-29 00:00:00 +0200404 public override async Task WriteUuidAsync(Guid uuid, CancellationToken cancellationToken = default)
405 {
406 await WriteStringAsync(uuid.ToString("D"), cancellationToken); // no curly braces
407 }
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100408
409 /// <summary>
410 /// Read in a JSON string, unescaping as appropriate.. Skip Reading from the
411 /// context if skipContext is true.
412 /// </summary>
Jens Geyer5a17b132019-05-26 15:53:37 +0200413 private async ValueTask<byte[]> ReadJsonStringAsync(bool skipContext, CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100414 {
415 using (var buffer = new MemoryStream())
416 {
417 var codeunits = new List<char>();
418
419
420 if (!skipContext)
421 {
Jens Geyer2ff952b2019-04-13 19:46:54 +0200422 await Context.ReadConditionalDelimiterAsync(cancellationToken);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100423 }
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 {
Jens Geyer0d128322021-02-25 09:42:52 +0100438#if NETSTANDARD2_0
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100439 await buffer.WriteAsync(new[] {ch}, 0, 1, cancellationToken);
Jens Geyer0d128322021-02-25 09:42:52 +0100440#else
441 var wbuf = new[] { ch };
442 await buffer.WriteAsync(wbuf.AsMemory(0, 1), cancellationToken);
443#endif
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100444 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];
Jens Geyer0d128322021-02-25 09:42:52 +0100457#if NETSTANDARD2_0
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100458 await buffer.WriteAsync(new[] {ch}, 0, 1, cancellationToken);
Jens Geyer0d128322021-02-25 09:42:52 +0100459#else
460 var wbuf = new[] { ch };
461 await buffer.WriteAsync( wbuf.AsMemory(0, 1), cancellationToken);
462#endif
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100463 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());
Jens Geyer0d128322021-02-25 09:42:52 +0100491#if NETSTANDARD2_0
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100492 await buffer.WriteAsync(tmp, 0, tmp.Length, cancellationToken);
Jens Geyer0d128322021-02-25 09:42:52 +0100493#else
494 await buffer.WriteAsync(tmp.AsMemory(0, tmp.Length), cancellationToken);
495#endif
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100496 codeunits.Clear();
497 }
498 else
499 {
Jens Geyer0d128322021-02-25 09:42:52 +0100500 var tmp = Utf8Encoding.GetBytes(new[] { (char)wch });
501#if NETSTANDARD2_0
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100502 await buffer.WriteAsync(tmp, 0, tmp.Length, cancellationToken);
Jens Geyer0d128322021-02-25 09:42:52 +0100503#else
504 await buffer.WriteAsync(tmp.AsMemory( 0, tmp.Length), cancellationToken);
505#endif
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100506 }
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>
Jens Geyer5a17b132019-05-26 15:53:37 +0200522 private async ValueTask<string> ReadJsonNumericCharsAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100523 {
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>
Jens Geyer5a17b132019-05-26 15:53:37 +0200549 private async ValueTask<long> ReadJsonIntegerAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100550 {
Jens Geyer2ff952b2019-04-13 19:46:54 +0200551 await Context.ReadConditionalDelimiterAsync(cancellationToken);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100552 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>
Jens Geyer5a17b132019-05-26 15:53:37 +0200577 private async ValueTask<double> ReadJsonDoubleAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100578 {
Jens Geyer2ff952b2019-04-13 19:46:54 +0200579 await Context.ReadConditionalDelimiterAsync(cancellationToken);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100580 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>
Jens Geyer5a17b132019-05-26 15:53:37 +0200613 private async ValueTask<byte[]> ReadJsonBase64Async(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100614 {
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
651 private async Task ReadJsonObjectStartAsync(CancellationToken cancellationToken)
652 {
Jens Geyer2ff952b2019-04-13 19:46:54 +0200653 await Context.ReadConditionalDelimiterAsync(cancellationToken);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100654 await ReadJsonSyntaxCharAsync(TJSONProtocolConstants.LeftBrace, cancellationToken);
655 PushContext(new JSONPairContext(this));
656 }
657
658 private async Task ReadJsonObjectEndAsync(CancellationToken cancellationToken)
659 {
660 await ReadJsonSyntaxCharAsync(TJSONProtocolConstants.RightBrace, cancellationToken);
661 PopContext();
662 }
663
664 private async Task ReadJsonArrayStartAsync(CancellationToken cancellationToken)
665 {
Jens Geyer2ff952b2019-04-13 19:46:54 +0200666 await Context.ReadConditionalDelimiterAsync(cancellationToken);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100667 await ReadJsonSyntaxCharAsync(TJSONProtocolConstants.LeftBracket, cancellationToken);
668 PushContext(new JSONListContext(this));
669 }
670
671 private async Task ReadJsonArrayEndAsync(CancellationToken cancellationToken)
672 {
673 await ReadJsonSyntaxCharAsync(TJSONProtocolConstants.RightBracket, cancellationToken);
674 PopContext();
675 }
676
Jens Geyer5a17b132019-05-26 15:53:37 +0200677 public override async ValueTask<TMessage> ReadMessageBeginAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100678 {
679 var message = new TMessage();
Paulo Nevesf049ff32020-02-05 11:58:18 +0100680
Jens Geyer0d128322021-02-25 09:42:52 +0100681 ResetContext();
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100682 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
695 public override async Task ReadMessageEndAsync(CancellationToken cancellationToken)
696 {
697 await ReadJsonArrayEndAsync(cancellationToken);
698 }
699
Jens Geyer5a17b132019-05-26 15:53:37 +0200700 public override async ValueTask<TStruct> ReadStructBeginAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100701 {
702 await ReadJsonObjectStartAsync(cancellationToken);
Jens Geyerdce22992020-05-16 23:02:27 +0200703
704 return AnonymousStruct;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100705 }
706
707 public override async Task ReadStructEndAsync(CancellationToken cancellationToken)
708 {
709 await ReadJsonObjectEndAsync(cancellationToken);
710 }
711
Jens Geyer5a17b132019-05-26 15:53:37 +0200712 public override async ValueTask<TField> ReadFieldBeginAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100713 {
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100714 var ch = await Reader.PeekAsync(cancellationToken);
715 if (ch == TJSONProtocolConstants.RightBrace[0])
716 {
Jens Geyerdce22992020-05-16 23:02:27 +0200717 return StopField;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100718 }
Jens Geyerdce22992020-05-16 23:02:27 +0200719
720 var field = new TField()
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100721 {
Jens Geyerdce22992020-05-16 23:02:27 +0200722 ID = (short)await ReadJsonIntegerAsync(cancellationToken)
723 };
724
725 await ReadJsonObjectStartAsync(cancellationToken);
726 field.Type = TJSONProtocolHelper.GetTypeIdForTypeName(await ReadJsonStringAsync(false, cancellationToken));
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100727 return field;
728 }
729
730 public override async Task ReadFieldEndAsync(CancellationToken cancellationToken)
731 {
732 await ReadJsonObjectEndAsync(cancellationToken);
733 }
734
Jens Geyer5a17b132019-05-26 15:53:37 +0200735 public override async ValueTask<TMap> ReadMapBeginAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100736 {
737 var map = new TMap();
738 await ReadJsonArrayStartAsync(cancellationToken);
739 map.KeyType = TJSONProtocolHelper.GetTypeIdForTypeName(await ReadJsonStringAsync(false, cancellationToken));
740 map.ValueType = TJSONProtocolHelper.GetTypeIdForTypeName(await ReadJsonStringAsync(false, cancellationToken));
741 map.Count = (int) await ReadJsonIntegerAsync(cancellationToken);
Jens Geyer50806452019-11-23 01:55:58 +0100742 CheckReadBytesAvailable(map);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100743 await ReadJsonObjectStartAsync(cancellationToken);
744 return map;
745 }
746
747 public override async Task ReadMapEndAsync(CancellationToken cancellationToken)
748 {
749 await ReadJsonObjectEndAsync(cancellationToken);
750 await ReadJsonArrayEndAsync(cancellationToken);
751 }
752
Jens Geyer5a17b132019-05-26 15:53:37 +0200753 public override async ValueTask<TList> ReadListBeginAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100754 {
755 var list = new TList();
756 await ReadJsonArrayStartAsync(cancellationToken);
757 list.ElementType = TJSONProtocolHelper.GetTypeIdForTypeName(await ReadJsonStringAsync(false, cancellationToken));
758 list.Count = (int) await ReadJsonIntegerAsync(cancellationToken);
Jens Geyer50806452019-11-23 01:55:58 +0100759 CheckReadBytesAvailable(list);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100760 return list;
761 }
762
763 public override async Task ReadListEndAsync(CancellationToken cancellationToken)
764 {
765 await ReadJsonArrayEndAsync(cancellationToken);
766 }
767
Jens Geyer5a17b132019-05-26 15:53:37 +0200768 public override async ValueTask<TSet> ReadSetBeginAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100769 {
770 var set = new TSet();
771 await ReadJsonArrayStartAsync(cancellationToken);
772 set.ElementType = TJSONProtocolHelper.GetTypeIdForTypeName(await ReadJsonStringAsync(false, cancellationToken));
773 set.Count = (int) await ReadJsonIntegerAsync(cancellationToken);
Jens Geyer50806452019-11-23 01:55:58 +0100774 CheckReadBytesAvailable(set);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100775 return set;
776 }
777
778 public override async Task ReadSetEndAsync(CancellationToken cancellationToken)
779 {
780 await ReadJsonArrayEndAsync(cancellationToken);
781 }
782
Jens Geyer5a17b132019-05-26 15:53:37 +0200783 public override async ValueTask<bool> ReadBoolAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100784 {
785 return await ReadJsonIntegerAsync(cancellationToken) != 0;
786 }
787
Jens Geyer5a17b132019-05-26 15:53:37 +0200788 public override async ValueTask<sbyte> ReadByteAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100789 {
790 return (sbyte) await ReadJsonIntegerAsync(cancellationToken);
791 }
792
Jens Geyer5a17b132019-05-26 15:53:37 +0200793 public override async ValueTask<short> ReadI16Async(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100794 {
795 return (short) await ReadJsonIntegerAsync(cancellationToken);
796 }
797
Jens Geyer5a17b132019-05-26 15:53:37 +0200798 public override async ValueTask<int> ReadI32Async(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100799 {
800 return (int) await ReadJsonIntegerAsync(cancellationToken);
801 }
802
Jens Geyer5a17b132019-05-26 15:53:37 +0200803 public override async ValueTask<long> ReadI64Async(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100804 {
805 return await ReadJsonIntegerAsync(cancellationToken);
806 }
807
Jens Geyer5a17b132019-05-26 15:53:37 +0200808 public override async ValueTask<double> ReadDoubleAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100809 {
810 return await ReadJsonDoubleAsync(cancellationToken);
811 }
812
Jens Geyer5a17b132019-05-26 15:53:37 +0200813 public override async ValueTask<string> ReadStringAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100814 {
815 var buf = await ReadJsonStringAsync(false, cancellationToken);
816 return Utf8Encoding.GetString(buf, 0, buf.Length);
817 }
818
Jens Geyer5a17b132019-05-26 15:53:37 +0200819 public override async ValueTask<byte[]> ReadBinaryAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100820 {
821 return await ReadJsonBase64Async(cancellationToken);
822 }
823
Jens Geyer62445c12022-06-29 00:00:00 +0200824 public override async ValueTask<Guid> ReadUuidAsync(CancellationToken cancellationToken = default)
825 {
826 return new Guid( await ReadStringAsync(cancellationToken));
827 }
828
Jens Geyer50806452019-11-23 01:55:58 +0100829 // Return the minimum number of bytes a type will consume on the wire
830 public override int GetMinSerializedSize(TType type)
831 {
832 switch (type)
833 {
834 case TType.Stop: return 0;
835 case TType.Void: return 0;
836 case TType.Bool: return 1; // written as int
837 case TType.Byte: return 1;
838 case TType.Double: return 1;
839 case TType.I16: return 1;
840 case TType.I32: return 1;
841 case TType.I64: return 1;
842 case TType.String: return 2; // empty string
843 case TType.Struct: return 2; // empty struct
844 case TType.Map: return 2; // empty map
845 case TType.Set: return 2; // empty set
846 case TType.List: return 2; // empty list
Jens Geyer62445c12022-06-29 00:00:00 +0200847 case TType.Uuid: return 36; // "E236974D-F0B0-4E05-8F29-0B455D41B1A1"
Jens Geyer0d128322021-02-25 09:42:52 +0100848 default: throw new TProtocolException(TProtocolException.NOT_IMPLEMENTED, "unrecognized type code");
Jens Geyer50806452019-11-23 01:55:58 +0100849 }
850 }
851
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100852 /// <summary>
853 /// Factory for JSON protocol objects
854 /// </summary>
Jens Geyer421444f2019-03-20 22:13:25 +0100855 public class Factory : TProtocolFactory
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100856 {
Jens Geyer421444f2019-03-20 22:13:25 +0100857 public override TProtocol GetProtocol(TTransport trans)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100858 {
859 return new TJsonProtocol(trans);
860 }
861 }
862
863 /// <summary>
864 /// Base class for tracking JSON contexts that may require
865 /// inserting/Reading additional JSON syntax characters
866 /// This base context does nothing.
867 /// </summary>
868 protected class JSONBaseContext
869 {
870 protected TJsonProtocol Proto;
871
872 public JSONBaseContext(TJsonProtocol proto)
873 {
874 Proto = proto;
875 }
876
Jens Geyerdce22992020-05-16 23:02:27 +0200877 public virtual Task WriteConditionalDelimiterAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100878 {
Jens Geyerdce22992020-05-16 23:02:27 +0200879 cancellationToken.ThrowIfCancellationRequested();
880 return Task.CompletedTask;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100881 }
882
Jens Geyerdce22992020-05-16 23:02:27 +0200883 public virtual Task ReadConditionalDelimiterAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100884 {
Jens Geyerdce22992020-05-16 23:02:27 +0200885 cancellationToken.ThrowIfCancellationRequested();
886 return Task.CompletedTask;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100887 }
888
889 public virtual bool EscapeNumbers()
890 {
891 return false;
892 }
893 }
894
895 /// <summary>
896 /// Context for JSON lists. Will insert/Read commas before each item except
897 /// for the first one
898 /// </summary>
899 protected class JSONListContext : JSONBaseContext
900 {
901 private bool _first = true;
902
903 public JSONListContext(TJsonProtocol protocol)
904 : base(protocol)
905 {
906 }
907
Jens Geyer2ff952b2019-04-13 19:46:54 +0200908 public override async Task WriteConditionalDelimiterAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100909 {
910 if (_first)
911 {
912 _first = false;
913 }
914 else
915 {
916 await Proto.Trans.WriteAsync(TJSONProtocolConstants.Comma, cancellationToken);
917 }
918 }
919
Jens Geyer2ff952b2019-04-13 19:46:54 +0200920 public override async Task ReadConditionalDelimiterAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100921 {
922 if (_first)
923 {
924 _first = false;
925 }
926 else
927 {
928 await Proto.ReadJsonSyntaxCharAsync(TJSONProtocolConstants.Comma, cancellationToken);
929 }
930 }
931 }
932
933 /// <summary>
934 /// Context for JSON records. Will insert/Read colons before the value portion
935 /// of each record pair, and commas before each key except the first. In
936 /// addition, will indicate that numbers in the key position need to be
937 /// escaped in quotes (since JSON keys must be strings).
938 /// </summary>
939 // ReSharper disable once InconsistentNaming
940 protected class JSONPairContext : JSONBaseContext
941 {
942 private bool _colon = true;
943
944 private bool _first = true;
945
946 public JSONPairContext(TJsonProtocol proto)
947 : base(proto)
948 {
949 }
950
Jens Geyer2ff952b2019-04-13 19:46:54 +0200951 public override async Task WriteConditionalDelimiterAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100952 {
953 if (_first)
954 {
955 _first = false;
956 _colon = true;
957 }
958 else
959 {
960 await Proto.Trans.WriteAsync(_colon ? TJSONProtocolConstants.Colon : TJSONProtocolConstants.Comma, cancellationToken);
961 _colon = !_colon;
962 }
963 }
964
Jens Geyer2ff952b2019-04-13 19:46:54 +0200965 public override async Task ReadConditionalDelimiterAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100966 {
967 if (_first)
968 {
969 _first = false;
970 _colon = true;
971 }
972 else
973 {
974 await Proto.ReadJsonSyntaxCharAsync(_colon ? TJSONProtocolConstants.Colon : TJSONProtocolConstants.Comma, cancellationToken);
975 _colon = !_colon;
976 }
977 }
978
979 public override bool EscapeNumbers()
980 {
981 return _colon;
982 }
983 }
984
985 /// <summary>
986 /// Holds up to one byte from the transport
987 /// </summary>
988 protected class LookaheadReader
989 {
990 private readonly byte[] _data = new byte[1];
991
992 private bool _hasData;
993 protected TJsonProtocol Proto;
994
995 public LookaheadReader(TJsonProtocol proto)
996 {
997 Proto = proto;
998 }
999
1000 /// <summary>
1001 /// Return and consume the next byte to be Read, either taking it from the
1002 /// data buffer if present or getting it from the transport otherwise.
1003 /// </summary>
Jens Geyer5a17b132019-05-26 15:53:37 +02001004 public async ValueTask<byte> ReadAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +01001005 {
Jens Geyerdce22992020-05-16 23:02:27 +02001006 cancellationToken.ThrowIfCancellationRequested();
Jens Geyeraa0c8b32019-01-28 23:27:45 +01001007
1008 if (_hasData)
1009 {
1010 _hasData = false;
1011 }
1012 else
1013 {
1014 // find more easy way to avoid exception on reading primitive types
1015 await Proto.Trans.ReadAllAsync(_data, 0, 1, cancellationToken);
1016 }
1017 return _data[0];
1018 }
1019
1020 /// <summary>
1021 /// Return the next byte to be Read without consuming, filling the data
1022 /// buffer if it has not been filled alReady.
1023 /// </summary>
Jens Geyer5a17b132019-05-26 15:53:37 +02001024 public async ValueTask<byte> PeekAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +01001025 {
Jens Geyerdce22992020-05-16 23:02:27 +02001026 cancellationToken.ThrowIfCancellationRequested();
Jens Geyeraa0c8b32019-01-28 23:27:45 +01001027
1028 if (!_hasData)
1029 {
1030 // find more easy way to avoid exception on reading primitive types
1031 await Proto.Trans.ReadAllAsync(_data, 0, 1, cancellationToken);
Jens Geyer5a17b132019-05-26 15:53:37 +02001032 _hasData = true;
Jens Geyeraa0c8b32019-01-28 23:27:45 +01001033 }
Jens Geyeraa0c8b32019-01-28 23:27:45 +01001034 return _data[0];
1035 }
1036 }
1037 }
1038}