blob: 64308d60be6440f44917828af165fd42a7d6dde6 [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 Geyer4115e952023-11-21 23:00:01 +010030#pragma warning disable IDE0079 // net20 - unneeded suppression
31#pragma warning disable IDE0290 // net8 - primary CTOR
Jens Geyer0d128322021-02-25 09:42:52 +010032
Jens Geyeraa0c8b32019-01-28 23:27:45 +010033namespace Thrift.Protocol
34{
35 /// <summary>
36 /// JSON protocol implementation for thrift.
37 /// This is a full-featured protocol supporting Write and Read.
38 /// Please see the C++ class header for a detailed description of the
39 /// protocol's wire format.
40 /// Adapted from the Java version.
41 /// </summary>
42 // ReSharper disable once InconsistentNaming
43 public class TJsonProtocol : TProtocol
44 {
45 private const long Version = 1;
46
47 // Temporary buffer used by several methods
48 private readonly byte[] _tempBuffer = new byte[4];
49
50 // Current context that we are in
51 protected JSONBaseContext Context;
52
53 // Stack of nested contexts that we may be in
54 protected Stack<JSONBaseContext> ContextStack = new Stack<JSONBaseContext>();
55
56 // Reader that manages a 1-byte buffer
57 protected LookaheadReader Reader;
58
59 // Default encoding
60 protected Encoding Utf8Encoding = Encoding.UTF8;
61
62 /// <summary>
63 /// TJsonProtocol Constructor
64 /// </summary>
65 public TJsonProtocol(TTransport trans)
66 : base(trans)
67 {
68 Context = new JSONBaseContext(this);
69 Reader = new LookaheadReader(this);
70 }
71
72 /// <summary>
73 /// Push a new JSON context onto the stack.
74 /// </summary>
75 protected void PushContext(JSONBaseContext c)
76 {
77 ContextStack.Push(Context);
78 Context = c;
79 }
80
81 /// <summary>
82 /// Pop the last JSON context off the stack
83 /// </summary>
84 protected void PopContext()
85 {
86 Context = ContextStack.Pop();
87 }
88
89 /// <summary>
Paulo Nevesf049ff32020-02-05 11:58:18 +010090 /// Resets the context stack to pristine state. Allows for reusal of the protocol
91 /// even in cases where the protocol instance was in an undefined state due to
92 /// dangling/stale/obsolete contexts
93 /// </summary>
Jens Geyer0d128322021-02-25 09:42:52 +010094 private void ResetContext()
Paulo Nevesf049ff32020-02-05 11:58:18 +010095 {
96 ContextStack.Clear();
97 Context = new JSONBaseContext(this);
98 }
99 /// <summary>
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100100 /// Read a byte that must match b[0]; otherwise an exception is thrown.
101 /// Marked protected to avoid synthetic accessor in JSONListContext.Read
102 /// and JSONPairContext.Read
103 /// </summary>
104 protected async Task ReadJsonSyntaxCharAsync(byte[] bytes, CancellationToken cancellationToken)
105 {
106 var ch = await Reader.ReadAsync(cancellationToken);
107 if (ch != bytes[0])
108 {
109 throw new TProtocolException(TProtocolException.INVALID_DATA, $"Unexpected character: {(char) ch}");
110 }
111 }
112
113 /// <summary>
114 /// Write the bytes in array buf as a JSON characters, escaping as needed
115 /// </summary>
116 private async Task WriteJsonStringAsync(byte[] bytes, CancellationToken cancellationToken)
117 {
Jens Geyer2ff952b2019-04-13 19:46:54 +0200118 await Context.WriteConditionalDelimiterAsync(cancellationToken);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100119 await Trans.WriteAsync(TJSONProtocolConstants.Quote, cancellationToken);
120
121 var len = bytes.Length;
122 for (var i = 0; i < len; i++)
123 {
124 if ((bytes[i] & 0x00FF) >= 0x30)
125 {
126 if (bytes[i] == TJSONProtocolConstants.Backslash[0])
127 {
128 await Trans.WriteAsync(TJSONProtocolConstants.Backslash, cancellationToken);
129 await Trans.WriteAsync(TJSONProtocolConstants.Backslash, cancellationToken);
130 }
131 else
132 {
Jens Geyerdce22992020-05-16 23:02:27 +0200133 await Trans.WriteAsync(bytes, i, 1, cancellationToken);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100134 }
135 }
136 else
137 {
138 _tempBuffer[0] = TJSONProtocolConstants.JsonCharTable[bytes[i]];
139 if (_tempBuffer[0] == 1)
140 {
141 await Trans.WriteAsync(bytes, i, 1, cancellationToken);
142 }
143 else if (_tempBuffer[0] > 1)
144 {
145 await Trans.WriteAsync(TJSONProtocolConstants.Backslash, cancellationToken);
146 await Trans.WriteAsync(_tempBuffer, 0, 1, cancellationToken);
147 }
148 else
149 {
150 await Trans.WriteAsync(TJSONProtocolConstants.EscSequences, cancellationToken);
151 _tempBuffer[0] = TJSONProtocolHelper.ToHexChar((byte) (bytes[i] >> 4));
152 _tempBuffer[1] = TJSONProtocolHelper.ToHexChar(bytes[i]);
153 await Trans.WriteAsync(_tempBuffer, 0, 2, cancellationToken);
154 }
155 }
156 }
157 await Trans.WriteAsync(TJSONProtocolConstants.Quote, cancellationToken);
158 }
159
160 /// <summary>
161 /// Write out number as a JSON value. If the context dictates so, it will be
162 /// wrapped in quotes to output as a JSON string.
163 /// </summary>
164 private async Task WriteJsonIntegerAsync(long num, CancellationToken cancellationToken)
165 {
Jens Geyer2ff952b2019-04-13 19:46:54 +0200166 await Context.WriteConditionalDelimiterAsync(cancellationToken);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100167 var str = num.ToString();
168
169 var escapeNum = Context.EscapeNumbers();
170 if (escapeNum)
171 {
172 await Trans.WriteAsync(TJSONProtocolConstants.Quote, cancellationToken);
173 }
174
175 var bytes = Utf8Encoding.GetBytes(str);
176 await Trans.WriteAsync(bytes, cancellationToken);
177
178 if (escapeNum)
179 {
180 await Trans.WriteAsync(TJSONProtocolConstants.Quote, cancellationToken);
181 }
182 }
183
184 /// <summary>
185 /// Write out a double as a JSON value. If it is NaN or infinity or if the
186 /// context dictates escaping, Write out as JSON string.
187 /// </summary>
188 private async Task WriteJsonDoubleAsync(double num, CancellationToken cancellationToken)
189 {
Jens Geyer2ff952b2019-04-13 19:46:54 +0200190 await Context.WriteConditionalDelimiterAsync(cancellationToken);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100191 var str = num.ToString("G17", CultureInfo.InvariantCulture);
192 var special = false;
193
194 switch (str[0])
195 {
196 case 'N': // NaN
197 case 'I': // Infinity
198 special = true;
199 break;
200 case '-':
201 if (str[1] == 'I')
202 {
203 // -Infinity
204 special = true;
205 }
206 break;
207 }
208
209 var escapeNum = special || Context.EscapeNumbers();
210
211 if (escapeNum)
212 {
213 await Trans.WriteAsync(TJSONProtocolConstants.Quote, cancellationToken);
214 }
215
216 await Trans.WriteAsync(Utf8Encoding.GetBytes(str), cancellationToken);
217
218 if (escapeNum)
219 {
220 await Trans.WriteAsync(TJSONProtocolConstants.Quote, cancellationToken);
221 }
222 }
223
224 /// <summary>
225 /// Write out contents of byte array b as a JSON string with base-64 encoded
226 /// data
227 /// </summary>
228 private async Task WriteJsonBase64Async(byte[] bytes, CancellationToken cancellationToken)
229 {
Jens Geyer2ff952b2019-04-13 19:46:54 +0200230 await Context.WriteConditionalDelimiterAsync(cancellationToken);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100231 await Trans.WriteAsync(TJSONProtocolConstants.Quote, cancellationToken);
232
233 var len = bytes.Length;
234 var off = 0;
235
236 while (len >= 3)
237 {
238 // Encode 3 bytes at a time
239 TBase64Utils.Encode(bytes, off, 3, _tempBuffer, 0);
240 await Trans.WriteAsync(_tempBuffer, 0, 4, cancellationToken);
241 off += 3;
242 len -= 3;
243 }
244
245 if (len > 0)
246 {
247 // Encode remainder
248 TBase64Utils.Encode(bytes, off, len, _tempBuffer, 0);
249 await Trans.WriteAsync(_tempBuffer, 0, len + 1, cancellationToken);
250 }
251
252 await Trans.WriteAsync(TJSONProtocolConstants.Quote, cancellationToken);
253 }
254
255 private async Task WriteJsonObjectStartAsync(CancellationToken cancellationToken)
256 {
Jens Geyer2ff952b2019-04-13 19:46:54 +0200257 await Context.WriteConditionalDelimiterAsync(cancellationToken);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100258 await Trans.WriteAsync(TJSONProtocolConstants.LeftBrace, cancellationToken);
259 PushContext(new JSONPairContext(this));
260 }
261
262 private async Task WriteJsonObjectEndAsync(CancellationToken cancellationToken)
263 {
264 PopContext();
265 await Trans.WriteAsync(TJSONProtocolConstants.RightBrace, cancellationToken);
266 }
267
268 private async Task WriteJsonArrayStartAsync(CancellationToken cancellationToken)
269 {
Jens Geyer2ff952b2019-04-13 19:46:54 +0200270 await Context.WriteConditionalDelimiterAsync(cancellationToken);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100271 await Trans.WriteAsync(TJSONProtocolConstants.LeftBracket, cancellationToken);
272 PushContext(new JSONListContext(this));
273 }
274
275 private async Task WriteJsonArrayEndAsync(CancellationToken cancellationToken)
276 {
277 PopContext();
278 await Trans.WriteAsync(TJSONProtocolConstants.RightBracket, cancellationToken);
279 }
280
281 public override async Task WriteMessageBeginAsync(TMessage message, CancellationToken cancellationToken)
282 {
Jens Geyer0d128322021-02-25 09:42:52 +0100283 ResetContext();
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100284 await WriteJsonArrayStartAsync(cancellationToken);
285 await WriteJsonIntegerAsync(Version, cancellationToken);
286
287 var b = Utf8Encoding.GetBytes(message.Name);
288 await WriteJsonStringAsync(b, cancellationToken);
289
290 await WriteJsonIntegerAsync((long) message.Type, cancellationToken);
291 await WriteJsonIntegerAsync(message.SeqID, cancellationToken);
292 }
293
294 public override async Task WriteMessageEndAsync(CancellationToken cancellationToken)
295 {
296 await WriteJsonArrayEndAsync(cancellationToken);
297 }
298
299 public override async Task WriteStructBeginAsync(TStruct @struct, CancellationToken cancellationToken)
300 {
301 await WriteJsonObjectStartAsync(cancellationToken);
302 }
303
304 public override async Task WriteStructEndAsync(CancellationToken cancellationToken)
305 {
306 await WriteJsonObjectEndAsync(cancellationToken);
307 }
308
309 public override async Task WriteFieldBeginAsync(TField field, CancellationToken cancellationToken)
310 {
311 await WriteJsonIntegerAsync(field.ID, cancellationToken);
312 await WriteJsonObjectStartAsync(cancellationToken);
313 await WriteJsonStringAsync(TJSONProtocolHelper.GetTypeNameForTypeId(field.Type), cancellationToken);
314 }
315
316 public override async Task WriteFieldEndAsync(CancellationToken cancellationToken)
317 {
318 await WriteJsonObjectEndAsync(cancellationToken);
319 }
320
Jens Geyerdce22992020-05-16 23:02:27 +0200321 public override Task WriteFieldStopAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100322 {
Jens Geyerdce22992020-05-16 23:02:27 +0200323 cancellationToken.ThrowIfCancellationRequested();
324 return Task.CompletedTask;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100325 }
326
327 public override async Task WriteMapBeginAsync(TMap map, CancellationToken cancellationToken)
328 {
329 await WriteJsonArrayStartAsync(cancellationToken);
330 await WriteJsonStringAsync(TJSONProtocolHelper.GetTypeNameForTypeId(map.KeyType), cancellationToken);
331 await WriteJsonStringAsync(TJSONProtocolHelper.GetTypeNameForTypeId(map.ValueType), cancellationToken);
332 await WriteJsonIntegerAsync(map.Count, cancellationToken);
333 await WriteJsonObjectStartAsync(cancellationToken);
334 }
335
336 public override async Task WriteMapEndAsync(CancellationToken cancellationToken)
337 {
338 await WriteJsonObjectEndAsync(cancellationToken);
339 await WriteJsonArrayEndAsync(cancellationToken);
340 }
341
342 public override async Task WriteListBeginAsync(TList list, CancellationToken cancellationToken)
343 {
344 await WriteJsonArrayStartAsync(cancellationToken);
345 await WriteJsonStringAsync(TJSONProtocolHelper.GetTypeNameForTypeId(list.ElementType), cancellationToken);
346 await WriteJsonIntegerAsync(list.Count, cancellationToken);
347 }
348
349 public override async Task WriteListEndAsync(CancellationToken cancellationToken)
350 {
351 await WriteJsonArrayEndAsync(cancellationToken);
352 }
353
354 public override async Task WriteSetBeginAsync(TSet set, CancellationToken cancellationToken)
355 {
356 await WriteJsonArrayStartAsync(cancellationToken);
357 await WriteJsonStringAsync(TJSONProtocolHelper.GetTypeNameForTypeId(set.ElementType), cancellationToken);
358 await WriteJsonIntegerAsync(set.Count, cancellationToken);
359 }
360
361 public override async Task WriteSetEndAsync(CancellationToken cancellationToken)
362 {
363 await WriteJsonArrayEndAsync(cancellationToken);
364 }
365
366 public override async Task WriteBoolAsync(bool b, CancellationToken cancellationToken)
367 {
368 await WriteJsonIntegerAsync(b ? 1 : 0, cancellationToken);
369 }
370
371 public override async Task WriteByteAsync(sbyte b, CancellationToken cancellationToken)
372 {
373 await WriteJsonIntegerAsync(b, cancellationToken);
374 }
375
376 public override async Task WriteI16Async(short i16, CancellationToken cancellationToken)
377 {
378 await WriteJsonIntegerAsync(i16, cancellationToken);
379 }
380
381 public override async Task WriteI32Async(int i32, CancellationToken cancellationToken)
382 {
383 await WriteJsonIntegerAsync(i32, cancellationToken);
384 }
385
386 public override async Task WriteI64Async(long i64, CancellationToken cancellationToken)
387 {
388 await WriteJsonIntegerAsync(i64, cancellationToken);
389 }
390
391 public override async Task WriteDoubleAsync(double d, CancellationToken cancellationToken)
392 {
393 await WriteJsonDoubleAsync(d, cancellationToken);
394 }
395
396 public override async Task WriteStringAsync(string s, CancellationToken cancellationToken)
397 {
398 var b = Utf8Encoding.GetBytes(s);
399 await WriteJsonStringAsync(b, cancellationToken);
400 }
401
402 public override async Task WriteBinaryAsync(byte[] bytes, CancellationToken cancellationToken)
403 {
404 await WriteJsonBase64Async(bytes, cancellationToken);
405 }
Jens Geyer62445c12022-06-29 00:00:00 +0200406 public override async Task WriteUuidAsync(Guid uuid, CancellationToken cancellationToken = default)
407 {
408 await WriteStringAsync(uuid.ToString("D"), cancellationToken); // no curly braces
409 }
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100410
411 /// <summary>
412 /// Read in a JSON string, unescaping as appropriate.. Skip Reading from the
413 /// context if skipContext is true.
414 /// </summary>
Jens Geyer5a17b132019-05-26 15:53:37 +0200415 private async ValueTask<byte[]> ReadJsonStringAsync(bool skipContext, CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100416 {
417 using (var buffer = new MemoryStream())
418 {
419 var codeunits = new List<char>();
420
421
422 if (!skipContext)
423 {
Jens Geyer2ff952b2019-04-13 19:46:54 +0200424 await Context.ReadConditionalDelimiterAsync(cancellationToken);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100425 }
426
427 await ReadJsonSyntaxCharAsync(TJSONProtocolConstants.Quote, cancellationToken);
428
429 while (true)
430 {
431 var ch = await Reader.ReadAsync(cancellationToken);
432 if (ch == TJSONProtocolConstants.Quote[0])
433 {
434 break;
435 }
436
437 // escaped?
438 if (ch != TJSONProtocolConstants.EscSequences[0])
439 {
Jens Geyer8e89abe2023-07-20 21:43:23 +0200440#if NET5_0_OR_GREATER
Jens Geyer0d128322021-02-25 09:42:52 +0100441 var wbuf = new[] { ch };
442 await buffer.WriteAsync(wbuf.AsMemory(0, 1), cancellationToken);
Jens Geyer8e89abe2023-07-20 21:43:23 +0200443#else
444 await buffer.WriteAsync(new[] { ch }, 0, 1, cancellationToken);
Jens Geyer0d128322021-02-25 09:42:52 +0100445#endif
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100446 continue;
447 }
448
449 // distinguish between \uXXXX and \?
450 ch = await Reader.ReadAsync(cancellationToken);
451 if (ch != TJSONProtocolConstants.EscSequences[1]) // control chars like \n
452 {
453 var off = Array.IndexOf(TJSONProtocolConstants.EscapeChars, (char) ch);
454 if (off == -1)
455 {
456 throw new TProtocolException(TProtocolException.INVALID_DATA, "Expected control char");
457 }
458 ch = TJSONProtocolConstants.EscapeCharValues[off];
Jens Geyer8e89abe2023-07-20 21:43:23 +0200459#if NET5_0_OR_GREATER
Jens Geyer0d128322021-02-25 09:42:52 +0100460 var wbuf = new[] { ch };
461 await buffer.WriteAsync( wbuf.AsMemory(0, 1), cancellationToken);
Jens Geyer8e89abe2023-07-20 21:43:23 +0200462#else
463 await buffer.WriteAsync(new[] { ch }, 0, 1, cancellationToken);
Jens Geyer0d128322021-02-25 09:42:52 +0100464#endif
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100465 continue;
466 }
467
468 // it's \uXXXX
469 await Trans.ReadAllAsync(_tempBuffer, 0, 4, cancellationToken);
470
471 var wch = (short) ((TJSONProtocolHelper.ToHexVal(_tempBuffer[0]) << 12) +
472 (TJSONProtocolHelper.ToHexVal(_tempBuffer[1]) << 8) +
473 (TJSONProtocolHelper.ToHexVal(_tempBuffer[2]) << 4) +
474 TJSONProtocolHelper.ToHexVal(_tempBuffer[3]));
475
476 if (char.IsHighSurrogate((char) wch))
477 {
478 if (codeunits.Count > 0)
479 {
480 throw new TProtocolException(TProtocolException.INVALID_DATA, "Expected low surrogate char");
481 }
482 codeunits.Add((char) wch);
483 }
484 else if (char.IsLowSurrogate((char) wch))
485 {
486 if (codeunits.Count == 0)
487 {
488 throw new TProtocolException(TProtocolException.INVALID_DATA, "Expected high surrogate char");
489 }
490
491 codeunits.Add((char) wch);
492 var tmp = Utf8Encoding.GetBytes(codeunits.ToArray());
Jens Geyer8e89abe2023-07-20 21:43:23 +0200493#if NET5_0_OR_GREATER
Jens Geyer0d128322021-02-25 09:42:52 +0100494 await buffer.WriteAsync(tmp.AsMemory(0, tmp.Length), cancellationToken);
Jens Geyer8e89abe2023-07-20 21:43:23 +0200495#else
496 await buffer.WriteAsync(tmp, 0, tmp.Length, cancellationToken);
Jens Geyer0d128322021-02-25 09:42:52 +0100497#endif
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100498 codeunits.Clear();
499 }
500 else
501 {
Jens Geyer0d128322021-02-25 09:42:52 +0100502 var tmp = Utf8Encoding.GetBytes(new[] { (char)wch });
Jens Geyer8e89abe2023-07-20 21:43:23 +0200503#if NET5_0_OR_GREATER
Jens Geyer0d128322021-02-25 09:42:52 +0100504 await buffer.WriteAsync(tmp.AsMemory( 0, tmp.Length), cancellationToken);
Jens Geyer8e89abe2023-07-20 21:43:23 +0200505#else
506 await buffer.WriteAsync(tmp, 0, tmp.Length, cancellationToken);
Jens Geyer0d128322021-02-25 09:42:52 +0100507#endif
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100508 }
509 }
510
511 if (codeunits.Count > 0)
512 {
513 throw new TProtocolException(TProtocolException.INVALID_DATA, "Expected low surrogate char");
514 }
515
516 return buffer.ToArray();
517 }
518 }
519
520 /// <summary>
521 /// Read in a sequence of characters that are all valid in JSON numbers. Does
522 /// not do a complete regex check to validate that this is actually a number.
523 /// </summary>
Jens Geyer5a17b132019-05-26 15:53:37 +0200524 private async ValueTask<string> ReadJsonNumericCharsAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100525 {
526 var strbld = new StringBuilder();
527 while (true)
528 {
529 //TODO: workaround for primitive types with TJsonProtocol, think - how to rewrite into more easy form without exceptions
530 try
531 {
532 var ch = await Reader.PeekAsync(cancellationToken);
533 if (!TJSONProtocolHelper.IsJsonNumeric(ch))
534 {
535 break;
536 }
537 var c = (char)await Reader.ReadAsync(cancellationToken);
538 strbld.Append(c);
539 }
540 catch (TTransportException)
541 {
542 break;
543 }
544 }
545 return strbld.ToString();
546 }
547
548 /// <summary>
549 /// Read in a JSON number. If the context dictates, Read in enclosing quotes.
550 /// </summary>
Jens Geyer5a17b132019-05-26 15:53:37 +0200551 private async ValueTask<long> ReadJsonIntegerAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100552 {
Jens Geyer2ff952b2019-04-13 19:46:54 +0200553 await Context.ReadConditionalDelimiterAsync(cancellationToken);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100554 if (Context.EscapeNumbers())
555 {
556 await ReadJsonSyntaxCharAsync(TJSONProtocolConstants.Quote, cancellationToken);
557 }
558
559 var str = await ReadJsonNumericCharsAsync(cancellationToken);
560 if (Context.EscapeNumbers())
561 {
562 await ReadJsonSyntaxCharAsync(TJSONProtocolConstants.Quote, cancellationToken);
563 }
564
565 try
566 {
567 return long.Parse(str);
568 }
569 catch (FormatException)
570 {
571 throw new TProtocolException(TProtocolException.INVALID_DATA, "Bad data encounted in numeric data");
572 }
573 }
574
575 /// <summary>
576 /// Read in a JSON double value. Throw if the value is not wrapped in quotes
577 /// when expected or if wrapped in quotes when not expected.
578 /// </summary>
Jens Geyer5a17b132019-05-26 15:53:37 +0200579 private async ValueTask<double> ReadJsonDoubleAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100580 {
Jens Geyer2ff952b2019-04-13 19:46:54 +0200581 await Context.ReadConditionalDelimiterAsync(cancellationToken);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100582 if (await Reader.PeekAsync(cancellationToken) == TJSONProtocolConstants.Quote[0])
583 {
584 var arr = await ReadJsonStringAsync(true, cancellationToken);
585 var dub = double.Parse(Utf8Encoding.GetString(arr, 0, arr.Length), CultureInfo.InvariantCulture);
586
587 if (!Context.EscapeNumbers() && !double.IsNaN(dub) && !double.IsInfinity(dub))
588 {
589 // Throw exception -- we should not be in a string in this case
590 throw new TProtocolException(TProtocolException.INVALID_DATA, "Numeric data unexpectedly quoted");
591 }
592
593 return dub;
594 }
595
596 if (Context.EscapeNumbers())
597 {
598 // This will throw - we should have had a quote if escapeNum == true
599 await ReadJsonSyntaxCharAsync(TJSONProtocolConstants.Quote, cancellationToken);
600 }
601
602 try
603 {
604 return double.Parse(await ReadJsonNumericCharsAsync(cancellationToken), CultureInfo.InvariantCulture);
605 }
606 catch (FormatException)
607 {
608 throw new TProtocolException(TProtocolException.INVALID_DATA, "Bad data encounted in numeric data");
609 }
610 }
611
612 /// <summary>
613 /// Read in a JSON string containing base-64 encoded data and decode it.
614 /// </summary>
Jens Geyer5a17b132019-05-26 15:53:37 +0200615 private async ValueTask<byte[]> ReadJsonBase64Async(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100616 {
617 var b = await ReadJsonStringAsync(false, cancellationToken);
618 var len = b.Length;
619 var off = 0;
620 var size = 0;
621
622 // reduce len to ignore fill bytes
623 while ((len > 0) && (b[len - 1] == '='))
624 {
625 --len;
626 }
627
628 // read & decode full byte triplets = 4 source bytes
629 while (len > 4)
630 {
631 // Decode 4 bytes at a time
632 TBase64Utils.Decode(b, off, 4, b, size); // NB: decoded in place
633 off += 4;
634 len -= 4;
635 size += 3;
636 }
637
638 // Don't decode if we hit the end or got a single leftover byte (invalid
639 // base64 but legal for skip of regular string exType)
640 if (len > 1)
641 {
642 // Decode remainder
643 TBase64Utils.Decode(b, off, len, b, size); // NB: decoded in place
644 size += len - 1;
645 }
646
647 // Sadly we must copy the byte[] (any way around this?)
648 var result = new byte[size];
649 Array.Copy(b, 0, result, 0, size);
650 return result;
651 }
652
653 private async Task ReadJsonObjectStartAsync(CancellationToken cancellationToken)
654 {
Jens Geyer2ff952b2019-04-13 19:46:54 +0200655 await Context.ReadConditionalDelimiterAsync(cancellationToken);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100656 await ReadJsonSyntaxCharAsync(TJSONProtocolConstants.LeftBrace, cancellationToken);
657 PushContext(new JSONPairContext(this));
658 }
659
660 private async Task ReadJsonObjectEndAsync(CancellationToken cancellationToken)
661 {
662 await ReadJsonSyntaxCharAsync(TJSONProtocolConstants.RightBrace, cancellationToken);
663 PopContext();
664 }
665
666 private async Task ReadJsonArrayStartAsync(CancellationToken cancellationToken)
667 {
Jens Geyer2ff952b2019-04-13 19:46:54 +0200668 await Context.ReadConditionalDelimiterAsync(cancellationToken);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100669 await ReadJsonSyntaxCharAsync(TJSONProtocolConstants.LeftBracket, cancellationToken);
670 PushContext(new JSONListContext(this));
671 }
672
673 private async Task ReadJsonArrayEndAsync(CancellationToken cancellationToken)
674 {
675 await ReadJsonSyntaxCharAsync(TJSONProtocolConstants.RightBracket, cancellationToken);
676 PopContext();
677 }
678
Jens Geyer5a17b132019-05-26 15:53:37 +0200679 public override async ValueTask<TMessage> ReadMessageBeginAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100680 {
681 var message = new TMessage();
Paulo Nevesf049ff32020-02-05 11:58:18 +0100682
Jens Geyer0d128322021-02-25 09:42:52 +0100683 ResetContext();
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100684 await ReadJsonArrayStartAsync(cancellationToken);
685 if (await ReadJsonIntegerAsync(cancellationToken) != Version)
686 {
687 throw new TProtocolException(TProtocolException.BAD_VERSION, "Message contained bad version.");
688 }
689
690 var buf = await ReadJsonStringAsync(false, cancellationToken);
691 message.Name = Utf8Encoding.GetString(buf, 0, buf.Length);
692 message.Type = (TMessageType) await ReadJsonIntegerAsync(cancellationToken);
693 message.SeqID = (int) await ReadJsonIntegerAsync(cancellationToken);
694 return message;
695 }
696
697 public override async Task ReadMessageEndAsync(CancellationToken cancellationToken)
698 {
Philip Lee2d2790f2022-09-15 12:43:03 +0100699 cancellationToken.ThrowIfCancellationRequested();
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100700 await ReadJsonArrayEndAsync(cancellationToken);
Philip Lee2d2790f2022-09-15 12:43:03 +0100701 Transport.ResetConsumedMessageSize();
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100702 }
703
Jens Geyer5a17b132019-05-26 15:53:37 +0200704 public override async ValueTask<TStruct> ReadStructBeginAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100705 {
706 await ReadJsonObjectStartAsync(cancellationToken);
Jens Geyerdce22992020-05-16 23:02:27 +0200707
708 return AnonymousStruct;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100709 }
710
711 public override async Task ReadStructEndAsync(CancellationToken cancellationToken)
712 {
713 await ReadJsonObjectEndAsync(cancellationToken);
714 }
715
Jens Geyer5a17b132019-05-26 15:53:37 +0200716 public override async ValueTask<TField> ReadFieldBeginAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100717 {
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100718 var ch = await Reader.PeekAsync(cancellationToken);
719 if (ch == TJSONProtocolConstants.RightBrace[0])
720 {
Jens Geyerdce22992020-05-16 23:02:27 +0200721 return StopField;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100722 }
Jens Geyerdce22992020-05-16 23:02:27 +0200723
724 var field = new TField()
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100725 {
Jens Geyerdce22992020-05-16 23:02:27 +0200726 ID = (short)await ReadJsonIntegerAsync(cancellationToken)
727 };
728
729 await ReadJsonObjectStartAsync(cancellationToken);
730 field.Type = TJSONProtocolHelper.GetTypeIdForTypeName(await ReadJsonStringAsync(false, cancellationToken));
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100731 return field;
732 }
733
734 public override async Task ReadFieldEndAsync(CancellationToken cancellationToken)
735 {
736 await ReadJsonObjectEndAsync(cancellationToken);
737 }
738
Jens Geyer5a17b132019-05-26 15:53:37 +0200739 public override async ValueTask<TMap> ReadMapBeginAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100740 {
741 var map = new TMap();
742 await ReadJsonArrayStartAsync(cancellationToken);
743 map.KeyType = TJSONProtocolHelper.GetTypeIdForTypeName(await ReadJsonStringAsync(false, cancellationToken));
744 map.ValueType = TJSONProtocolHelper.GetTypeIdForTypeName(await ReadJsonStringAsync(false, cancellationToken));
745 map.Count = (int) await ReadJsonIntegerAsync(cancellationToken);
Jens Geyer50806452019-11-23 01:55:58 +0100746 CheckReadBytesAvailable(map);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100747 await ReadJsonObjectStartAsync(cancellationToken);
748 return map;
749 }
750
751 public override async Task ReadMapEndAsync(CancellationToken cancellationToken)
752 {
753 await ReadJsonObjectEndAsync(cancellationToken);
754 await ReadJsonArrayEndAsync(cancellationToken);
755 }
756
Jens Geyer5a17b132019-05-26 15:53:37 +0200757 public override async ValueTask<TList> ReadListBeginAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100758 {
759 var list = new TList();
760 await ReadJsonArrayStartAsync(cancellationToken);
761 list.ElementType = TJSONProtocolHelper.GetTypeIdForTypeName(await ReadJsonStringAsync(false, cancellationToken));
762 list.Count = (int) await ReadJsonIntegerAsync(cancellationToken);
Jens Geyer50806452019-11-23 01:55:58 +0100763 CheckReadBytesAvailable(list);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100764 return list;
765 }
766
767 public override async Task ReadListEndAsync(CancellationToken cancellationToken)
768 {
769 await ReadJsonArrayEndAsync(cancellationToken);
770 }
771
Jens Geyer5a17b132019-05-26 15:53:37 +0200772 public override async ValueTask<TSet> ReadSetBeginAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100773 {
774 var set = new TSet();
775 await ReadJsonArrayStartAsync(cancellationToken);
776 set.ElementType = TJSONProtocolHelper.GetTypeIdForTypeName(await ReadJsonStringAsync(false, cancellationToken));
777 set.Count = (int) await ReadJsonIntegerAsync(cancellationToken);
Jens Geyer50806452019-11-23 01:55:58 +0100778 CheckReadBytesAvailable(set);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100779 return set;
780 }
781
782 public override async Task ReadSetEndAsync(CancellationToken cancellationToken)
783 {
784 await ReadJsonArrayEndAsync(cancellationToken);
785 }
786
Jens Geyer5a17b132019-05-26 15:53:37 +0200787 public override async ValueTask<bool> ReadBoolAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100788 {
789 return await ReadJsonIntegerAsync(cancellationToken) != 0;
790 }
791
Jens Geyer5a17b132019-05-26 15:53:37 +0200792 public override async ValueTask<sbyte> ReadByteAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100793 {
794 return (sbyte) await ReadJsonIntegerAsync(cancellationToken);
795 }
796
Jens Geyer5a17b132019-05-26 15:53:37 +0200797 public override async ValueTask<short> ReadI16Async(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100798 {
799 return (short) await ReadJsonIntegerAsync(cancellationToken);
800 }
801
Jens Geyer5a17b132019-05-26 15:53:37 +0200802 public override async ValueTask<int> ReadI32Async(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100803 {
804 return (int) await ReadJsonIntegerAsync(cancellationToken);
805 }
806
Jens Geyer5a17b132019-05-26 15:53:37 +0200807 public override async ValueTask<long> ReadI64Async(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100808 {
809 return await ReadJsonIntegerAsync(cancellationToken);
810 }
811
Jens Geyer5a17b132019-05-26 15:53:37 +0200812 public override async ValueTask<double> ReadDoubleAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100813 {
814 return await ReadJsonDoubleAsync(cancellationToken);
815 }
816
Jens Geyer5a17b132019-05-26 15:53:37 +0200817 public override async ValueTask<string> ReadStringAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100818 {
819 var buf = await ReadJsonStringAsync(false, cancellationToken);
820 return Utf8Encoding.GetString(buf, 0, buf.Length);
821 }
822
Jens Geyer5a17b132019-05-26 15:53:37 +0200823 public override async ValueTask<byte[]> ReadBinaryAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100824 {
825 return await ReadJsonBase64Async(cancellationToken);
826 }
827
Jens Geyer62445c12022-06-29 00:00:00 +0200828 public override async ValueTask<Guid> ReadUuidAsync(CancellationToken cancellationToken = default)
829 {
830 return new Guid( await ReadStringAsync(cancellationToken));
831 }
832
Jens Geyer50806452019-11-23 01:55:58 +0100833 // Return the minimum number of bytes a type will consume on the wire
834 public override int GetMinSerializedSize(TType type)
835 {
836 switch (type)
837 {
838 case TType.Stop: return 0;
839 case TType.Void: return 0;
840 case TType.Bool: return 1; // written as int
841 case TType.Byte: return 1;
842 case TType.Double: return 1;
843 case TType.I16: return 1;
844 case TType.I32: return 1;
845 case TType.I64: return 1;
846 case TType.String: return 2; // empty string
847 case TType.Struct: return 2; // empty struct
848 case TType.Map: return 2; // empty map
849 case TType.Set: return 2; // empty set
850 case TType.List: return 2; // empty list
Jens Geyer62445c12022-06-29 00:00:00 +0200851 case TType.Uuid: return 36; // "E236974D-F0B0-4E05-8F29-0B455D41B1A1"
Jens Geyer0d128322021-02-25 09:42:52 +0100852 default: throw new TProtocolException(TProtocolException.NOT_IMPLEMENTED, "unrecognized type code");
Jens Geyer50806452019-11-23 01:55:58 +0100853 }
854 }
855
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100856 /// <summary>
857 /// Factory for JSON protocol objects
858 /// </summary>
Jens Geyer421444f2019-03-20 22:13:25 +0100859 public class Factory : TProtocolFactory
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100860 {
Jens Geyer421444f2019-03-20 22:13:25 +0100861 public override TProtocol GetProtocol(TTransport trans)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100862 {
863 return new TJsonProtocol(trans);
864 }
865 }
866
867 /// <summary>
868 /// Base class for tracking JSON contexts that may require
869 /// inserting/Reading additional JSON syntax characters
870 /// This base context does nothing.
871 /// </summary>
872 protected class JSONBaseContext
873 {
874 protected TJsonProtocol Proto;
875
876 public JSONBaseContext(TJsonProtocol proto)
877 {
878 Proto = proto;
879 }
880
Jens Geyerdce22992020-05-16 23:02:27 +0200881 public virtual Task WriteConditionalDelimiterAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100882 {
Jens Geyerdce22992020-05-16 23:02:27 +0200883 cancellationToken.ThrowIfCancellationRequested();
884 return Task.CompletedTask;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100885 }
886
Jens Geyerdce22992020-05-16 23:02:27 +0200887 public virtual Task ReadConditionalDelimiterAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100888 {
Jens Geyerdce22992020-05-16 23:02:27 +0200889 cancellationToken.ThrowIfCancellationRequested();
890 return Task.CompletedTask;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100891 }
892
893 public virtual bool EscapeNumbers()
894 {
895 return false;
896 }
897 }
898
899 /// <summary>
900 /// Context for JSON lists. Will insert/Read commas before each item except
901 /// for the first one
902 /// </summary>
903 protected class JSONListContext : JSONBaseContext
904 {
905 private bool _first = true;
906
907 public JSONListContext(TJsonProtocol protocol)
908 : base(protocol)
909 {
910 }
911
Jens Geyer2ff952b2019-04-13 19:46:54 +0200912 public override async Task WriteConditionalDelimiterAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100913 {
914 if (_first)
915 {
916 _first = false;
917 }
918 else
919 {
920 await Proto.Trans.WriteAsync(TJSONProtocolConstants.Comma, cancellationToken);
921 }
922 }
923
Jens Geyer2ff952b2019-04-13 19:46:54 +0200924 public override async Task ReadConditionalDelimiterAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100925 {
926 if (_first)
927 {
928 _first = false;
929 }
930 else
931 {
932 await Proto.ReadJsonSyntaxCharAsync(TJSONProtocolConstants.Comma, cancellationToken);
933 }
934 }
935 }
936
937 /// <summary>
938 /// Context for JSON records. Will insert/Read colons before the value portion
939 /// of each record pair, and commas before each key except the first. In
940 /// addition, will indicate that numbers in the key position need to be
941 /// escaped in quotes (since JSON keys must be strings).
942 /// </summary>
943 // ReSharper disable once InconsistentNaming
944 protected class JSONPairContext : JSONBaseContext
945 {
946 private bool _colon = true;
947
948 private bool _first = true;
949
950 public JSONPairContext(TJsonProtocol proto)
951 : base(proto)
952 {
953 }
954
Jens Geyer2ff952b2019-04-13 19:46:54 +0200955 public override async Task WriteConditionalDelimiterAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100956 {
957 if (_first)
958 {
959 _first = false;
960 _colon = true;
961 }
962 else
963 {
964 await Proto.Trans.WriteAsync(_colon ? TJSONProtocolConstants.Colon : TJSONProtocolConstants.Comma, cancellationToken);
965 _colon = !_colon;
966 }
967 }
968
Jens Geyer2ff952b2019-04-13 19:46:54 +0200969 public override async Task ReadConditionalDelimiterAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100970 {
971 if (_first)
972 {
973 _first = false;
974 _colon = true;
975 }
976 else
977 {
978 await Proto.ReadJsonSyntaxCharAsync(_colon ? TJSONProtocolConstants.Colon : TJSONProtocolConstants.Comma, cancellationToken);
979 _colon = !_colon;
980 }
981 }
982
983 public override bool EscapeNumbers()
984 {
985 return _colon;
986 }
987 }
988
989 /// <summary>
990 /// Holds up to one byte from the transport
991 /// </summary>
992 protected class LookaheadReader
993 {
994 private readonly byte[] _data = new byte[1];
995
996 private bool _hasData;
997 protected TJsonProtocol Proto;
998
999 public LookaheadReader(TJsonProtocol proto)
1000 {
1001 Proto = proto;
1002 }
1003
1004 /// <summary>
1005 /// Return and consume the next byte to be Read, either taking it from the
1006 /// data buffer if present or getting it from the transport otherwise.
1007 /// </summary>
Jens Geyer5a17b132019-05-26 15:53:37 +02001008 public async ValueTask<byte> ReadAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +01001009 {
Jens Geyerdce22992020-05-16 23:02:27 +02001010 cancellationToken.ThrowIfCancellationRequested();
Jens Geyeraa0c8b32019-01-28 23:27:45 +01001011
1012 if (_hasData)
1013 {
1014 _hasData = false;
1015 }
1016 else
1017 {
1018 // find more easy way to avoid exception on reading primitive types
1019 await Proto.Trans.ReadAllAsync(_data, 0, 1, cancellationToken);
1020 }
1021 return _data[0];
1022 }
1023
1024 /// <summary>
1025 /// Return the next byte to be Read without consuming, filling the data
1026 /// buffer if it has not been filled alReady.
1027 /// </summary>
Jens Geyer5a17b132019-05-26 15:53:37 +02001028 public async ValueTask<byte> PeekAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +01001029 {
Jens Geyerdce22992020-05-16 23:02:27 +02001030 cancellationToken.ThrowIfCancellationRequested();
Jens Geyeraa0c8b32019-01-28 23:27:45 +01001031
1032 if (!_hasData)
1033 {
1034 // find more easy way to avoid exception on reading primitive types
1035 await Proto.Trans.ReadAllAsync(_data, 0, 1, cancellationToken);
Jens Geyer5a17b132019-05-26 15:53:37 +02001036 _hasData = true;
Jens Geyeraa0c8b32019-01-28 23:27:45 +01001037 }
Jens Geyeraa0c8b32019-01-28 23:27:45 +01001038 return _data[0];
1039 }
1040 }
1041 }
1042}