blob: 82e758b1ee95a9cb5c224257fcabd3b936dc2659 [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 {
Philip Lee2d2790f2022-09-15 12:43:03 +0100697 cancellationToken.ThrowIfCancellationRequested();
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100698 await ReadJsonArrayEndAsync(cancellationToken);
Philip Lee2d2790f2022-09-15 12:43:03 +0100699 Transport.ResetConsumedMessageSize();
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100700 }
701
Jens Geyer5a17b132019-05-26 15:53:37 +0200702 public override async ValueTask<TStruct> ReadStructBeginAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100703 {
704 await ReadJsonObjectStartAsync(cancellationToken);
Jens Geyerdce22992020-05-16 23:02:27 +0200705
706 return AnonymousStruct;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100707 }
708
709 public override async Task ReadStructEndAsync(CancellationToken cancellationToken)
710 {
711 await ReadJsonObjectEndAsync(cancellationToken);
712 }
713
Jens Geyer5a17b132019-05-26 15:53:37 +0200714 public override async ValueTask<TField> ReadFieldBeginAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100715 {
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100716 var ch = await Reader.PeekAsync(cancellationToken);
717 if (ch == TJSONProtocolConstants.RightBrace[0])
718 {
Jens Geyerdce22992020-05-16 23:02:27 +0200719 return StopField;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100720 }
Jens Geyerdce22992020-05-16 23:02:27 +0200721
722 var field = new TField()
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100723 {
Jens Geyerdce22992020-05-16 23:02:27 +0200724 ID = (short)await ReadJsonIntegerAsync(cancellationToken)
725 };
726
727 await ReadJsonObjectStartAsync(cancellationToken);
728 field.Type = TJSONProtocolHelper.GetTypeIdForTypeName(await ReadJsonStringAsync(false, cancellationToken));
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100729 return field;
730 }
731
732 public override async Task ReadFieldEndAsync(CancellationToken cancellationToken)
733 {
734 await ReadJsonObjectEndAsync(cancellationToken);
735 }
736
Jens Geyer5a17b132019-05-26 15:53:37 +0200737 public override async ValueTask<TMap> ReadMapBeginAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100738 {
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);
Jens Geyer50806452019-11-23 01:55:58 +0100744 CheckReadBytesAvailable(map);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100745 await ReadJsonObjectStartAsync(cancellationToken);
746 return map;
747 }
748
749 public override async Task ReadMapEndAsync(CancellationToken cancellationToken)
750 {
751 await ReadJsonObjectEndAsync(cancellationToken);
752 await ReadJsonArrayEndAsync(cancellationToken);
753 }
754
Jens Geyer5a17b132019-05-26 15:53:37 +0200755 public override async ValueTask<TList> ReadListBeginAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100756 {
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);
Jens Geyer50806452019-11-23 01:55:58 +0100761 CheckReadBytesAvailable(list);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100762 return list;
763 }
764
765 public override async Task ReadListEndAsync(CancellationToken cancellationToken)
766 {
767 await ReadJsonArrayEndAsync(cancellationToken);
768 }
769
Jens Geyer5a17b132019-05-26 15:53:37 +0200770 public override async ValueTask<TSet> ReadSetBeginAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100771 {
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);
Jens Geyer50806452019-11-23 01:55:58 +0100776 CheckReadBytesAvailable(set);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100777 return set;
778 }
779
780 public override async Task ReadSetEndAsync(CancellationToken cancellationToken)
781 {
782 await ReadJsonArrayEndAsync(cancellationToken);
783 }
784
Jens Geyer5a17b132019-05-26 15:53:37 +0200785 public override async ValueTask<bool> ReadBoolAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100786 {
787 return await ReadJsonIntegerAsync(cancellationToken) != 0;
788 }
789
Jens Geyer5a17b132019-05-26 15:53:37 +0200790 public override async ValueTask<sbyte> ReadByteAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100791 {
792 return (sbyte) await ReadJsonIntegerAsync(cancellationToken);
793 }
794
Jens Geyer5a17b132019-05-26 15:53:37 +0200795 public override async ValueTask<short> ReadI16Async(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100796 {
797 return (short) await ReadJsonIntegerAsync(cancellationToken);
798 }
799
Jens Geyer5a17b132019-05-26 15:53:37 +0200800 public override async ValueTask<int> ReadI32Async(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100801 {
802 return (int) await ReadJsonIntegerAsync(cancellationToken);
803 }
804
Jens Geyer5a17b132019-05-26 15:53:37 +0200805 public override async ValueTask<long> ReadI64Async(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100806 {
807 return await ReadJsonIntegerAsync(cancellationToken);
808 }
809
Jens Geyer5a17b132019-05-26 15:53:37 +0200810 public override async ValueTask<double> ReadDoubleAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100811 {
812 return await ReadJsonDoubleAsync(cancellationToken);
813 }
814
Jens Geyer5a17b132019-05-26 15:53:37 +0200815 public override async ValueTask<string> ReadStringAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100816 {
817 var buf = await ReadJsonStringAsync(false, cancellationToken);
818 return Utf8Encoding.GetString(buf, 0, buf.Length);
819 }
820
Jens Geyer5a17b132019-05-26 15:53:37 +0200821 public override async ValueTask<byte[]> ReadBinaryAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100822 {
823 return await ReadJsonBase64Async(cancellationToken);
824 }
825
Jens Geyer62445c12022-06-29 00:00:00 +0200826 public override async ValueTask<Guid> ReadUuidAsync(CancellationToken cancellationToken = default)
827 {
828 return new Guid( await ReadStringAsync(cancellationToken));
829 }
830
Jens Geyer50806452019-11-23 01:55:58 +0100831 // Return the minimum number of bytes a type will consume on the wire
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
Jens Geyer62445c12022-06-29 00:00:00 +0200849 case TType.Uuid: return 36; // "E236974D-F0B0-4E05-8F29-0B455D41B1A1"
Jens Geyer0d128322021-02-25 09:42:52 +0100850 default: throw new TProtocolException(TProtocolException.NOT_IMPLEMENTED, "unrecognized type code");
Jens Geyer50806452019-11-23 01:55:58 +0100851 }
852 }
853
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100854 /// <summary>
855 /// Factory for JSON protocol objects
856 /// </summary>
Jens Geyer421444f2019-03-20 22:13:25 +0100857 public class Factory : TProtocolFactory
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100858 {
Jens Geyer421444f2019-03-20 22:13:25 +0100859 public override TProtocol GetProtocol(TTransport trans)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100860 {
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
874 public JSONBaseContext(TJsonProtocol proto)
875 {
876 Proto = proto;
877 }
878
Jens Geyerdce22992020-05-16 23:02:27 +0200879 public virtual Task WriteConditionalDelimiterAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100880 {
Jens Geyerdce22992020-05-16 23:02:27 +0200881 cancellationToken.ThrowIfCancellationRequested();
882 return Task.CompletedTask;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100883 }
884
Jens Geyerdce22992020-05-16 23:02:27 +0200885 public virtual Task ReadConditionalDelimiterAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100886 {
Jens Geyerdce22992020-05-16 23:02:27 +0200887 cancellationToken.ThrowIfCancellationRequested();
888 return Task.CompletedTask;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100889 }
890
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
905 public JSONListContext(TJsonProtocol protocol)
906 : base(protocol)
907 {
908 }
909
Jens Geyer2ff952b2019-04-13 19:46:54 +0200910 public override async Task WriteConditionalDelimiterAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100911 {
912 if (_first)
913 {
914 _first = false;
915 }
916 else
917 {
918 await Proto.Trans.WriteAsync(TJSONProtocolConstants.Comma, cancellationToken);
919 }
920 }
921
Jens Geyer2ff952b2019-04-13 19:46:54 +0200922 public override async Task ReadConditionalDelimiterAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100923 {
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
948 public JSONPairContext(TJsonProtocol proto)
949 : base(proto)
950 {
951 }
952
Jens Geyer2ff952b2019-04-13 19:46:54 +0200953 public override async Task WriteConditionalDelimiterAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100954 {
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
Jens Geyer2ff952b2019-04-13 19:46:54 +0200967 public override async Task ReadConditionalDelimiterAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100968 {
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
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
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>
Jens Geyer5a17b132019-05-26 15:53:37 +02001006 public async ValueTask<byte> ReadAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +01001007 {
Jens Geyerdce22992020-05-16 23:02:27 +02001008 cancellationToken.ThrowIfCancellationRequested();
Jens Geyeraa0c8b32019-01-28 23:27:45 +01001009
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>
Jens Geyer5a17b132019-05-26 15:53:37 +02001026 public async ValueTask<byte> PeekAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +01001027 {
Jens Geyerdce22992020-05-16 23:02:27 +02001028 cancellationToken.ThrowIfCancellationRequested();
Jens Geyeraa0c8b32019-01-28 23:27:45 +01001029
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);
Jens Geyer5a17b132019-05-26 15:53:37 +02001034 _hasData = true;
Jens Geyeraa0c8b32019-01-28 23:27:45 +01001035 }
Jens Geyeraa0c8b32019-01-28 23:27:45 +01001036 return _data[0];
1037 }
1038 }
1039 }
1040}