blob: 8799026bad995bf8b810002f1c833e45c2471504 [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 }
404
405 /// <summary>
406 /// Read in a JSON string, unescaping as appropriate.. Skip Reading from the
407 /// context if skipContext is true.
408 /// </summary>
Jens Geyer5a17b132019-05-26 15:53:37 +0200409 private async ValueTask<byte[]> ReadJsonStringAsync(bool skipContext, CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100410 {
411 using (var buffer = new MemoryStream())
412 {
413 var codeunits = new List<char>();
414
415
416 if (!skipContext)
417 {
Jens Geyer2ff952b2019-04-13 19:46:54 +0200418 await Context.ReadConditionalDelimiterAsync(cancellationToken);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100419 }
420
421 await ReadJsonSyntaxCharAsync(TJSONProtocolConstants.Quote, cancellationToken);
422
423 while (true)
424 {
425 var ch = await Reader.ReadAsync(cancellationToken);
426 if (ch == TJSONProtocolConstants.Quote[0])
427 {
428 break;
429 }
430
431 // escaped?
432 if (ch != TJSONProtocolConstants.EscSequences[0])
433 {
Jens Geyer0d128322021-02-25 09:42:52 +0100434#if NETSTANDARD2_0
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100435 await buffer.WriteAsync(new[] {ch}, 0, 1, cancellationToken);
Jens Geyer0d128322021-02-25 09:42:52 +0100436#else
437 var wbuf = new[] { ch };
438 await buffer.WriteAsync(wbuf.AsMemory(0, 1), cancellationToken);
439#endif
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100440 continue;
441 }
442
443 // distinguish between \uXXXX and \?
444 ch = await Reader.ReadAsync(cancellationToken);
445 if (ch != TJSONProtocolConstants.EscSequences[1]) // control chars like \n
446 {
447 var off = Array.IndexOf(TJSONProtocolConstants.EscapeChars, (char) ch);
448 if (off == -1)
449 {
450 throw new TProtocolException(TProtocolException.INVALID_DATA, "Expected control char");
451 }
452 ch = TJSONProtocolConstants.EscapeCharValues[off];
Jens Geyer0d128322021-02-25 09:42:52 +0100453#if NETSTANDARD2_0
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100454 await buffer.WriteAsync(new[] {ch}, 0, 1, cancellationToken);
Jens Geyer0d128322021-02-25 09:42:52 +0100455#else
456 var wbuf = new[] { ch };
457 await buffer.WriteAsync( wbuf.AsMemory(0, 1), cancellationToken);
458#endif
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100459 continue;
460 }
461
462 // it's \uXXXX
463 await Trans.ReadAllAsync(_tempBuffer, 0, 4, cancellationToken);
464
465 var wch = (short) ((TJSONProtocolHelper.ToHexVal(_tempBuffer[0]) << 12) +
466 (TJSONProtocolHelper.ToHexVal(_tempBuffer[1]) << 8) +
467 (TJSONProtocolHelper.ToHexVal(_tempBuffer[2]) << 4) +
468 TJSONProtocolHelper.ToHexVal(_tempBuffer[3]));
469
470 if (char.IsHighSurrogate((char) wch))
471 {
472 if (codeunits.Count > 0)
473 {
474 throw new TProtocolException(TProtocolException.INVALID_DATA, "Expected low surrogate char");
475 }
476 codeunits.Add((char) wch);
477 }
478 else if (char.IsLowSurrogate((char) wch))
479 {
480 if (codeunits.Count == 0)
481 {
482 throw new TProtocolException(TProtocolException.INVALID_DATA, "Expected high surrogate char");
483 }
484
485 codeunits.Add((char) wch);
486 var tmp = Utf8Encoding.GetBytes(codeunits.ToArray());
Jens Geyer0d128322021-02-25 09:42:52 +0100487#if NETSTANDARD2_0
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100488 await buffer.WriteAsync(tmp, 0, tmp.Length, cancellationToken);
Jens Geyer0d128322021-02-25 09:42:52 +0100489#else
490 await buffer.WriteAsync(tmp.AsMemory(0, tmp.Length), cancellationToken);
491#endif
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100492 codeunits.Clear();
493 }
494 else
495 {
Jens Geyer0d128322021-02-25 09:42:52 +0100496 var tmp = Utf8Encoding.GetBytes(new[] { (char)wch });
497#if NETSTANDARD2_0
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100498 await buffer.WriteAsync(tmp, 0, tmp.Length, cancellationToken);
Jens Geyer0d128322021-02-25 09:42:52 +0100499#else
500 await buffer.WriteAsync(tmp.AsMemory( 0, tmp.Length), cancellationToken);
501#endif
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100502 }
503 }
504
505 if (codeunits.Count > 0)
506 {
507 throw new TProtocolException(TProtocolException.INVALID_DATA, "Expected low surrogate char");
508 }
509
510 return buffer.ToArray();
511 }
512 }
513
514 /// <summary>
515 /// Read in a sequence of characters that are all valid in JSON numbers. Does
516 /// not do a complete regex check to validate that this is actually a number.
517 /// </summary>
Jens Geyer5a17b132019-05-26 15:53:37 +0200518 private async ValueTask<string> ReadJsonNumericCharsAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100519 {
520 var strbld = new StringBuilder();
521 while (true)
522 {
523 //TODO: workaround for primitive types with TJsonProtocol, think - how to rewrite into more easy form without exceptions
524 try
525 {
526 var ch = await Reader.PeekAsync(cancellationToken);
527 if (!TJSONProtocolHelper.IsJsonNumeric(ch))
528 {
529 break;
530 }
531 var c = (char)await Reader.ReadAsync(cancellationToken);
532 strbld.Append(c);
533 }
534 catch (TTransportException)
535 {
536 break;
537 }
538 }
539 return strbld.ToString();
540 }
541
542 /// <summary>
543 /// Read in a JSON number. If the context dictates, Read in enclosing quotes.
544 /// </summary>
Jens Geyer5a17b132019-05-26 15:53:37 +0200545 private async ValueTask<long> ReadJsonIntegerAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100546 {
Jens Geyer2ff952b2019-04-13 19:46:54 +0200547 await Context.ReadConditionalDelimiterAsync(cancellationToken);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100548 if (Context.EscapeNumbers())
549 {
550 await ReadJsonSyntaxCharAsync(TJSONProtocolConstants.Quote, cancellationToken);
551 }
552
553 var str = await ReadJsonNumericCharsAsync(cancellationToken);
554 if (Context.EscapeNumbers())
555 {
556 await ReadJsonSyntaxCharAsync(TJSONProtocolConstants.Quote, cancellationToken);
557 }
558
559 try
560 {
561 return long.Parse(str);
562 }
563 catch (FormatException)
564 {
565 throw new TProtocolException(TProtocolException.INVALID_DATA, "Bad data encounted in numeric data");
566 }
567 }
568
569 /// <summary>
570 /// Read in a JSON double value. Throw if the value is not wrapped in quotes
571 /// when expected or if wrapped in quotes when not expected.
572 /// </summary>
Jens Geyer5a17b132019-05-26 15:53:37 +0200573 private async ValueTask<double> ReadJsonDoubleAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100574 {
Jens Geyer2ff952b2019-04-13 19:46:54 +0200575 await Context.ReadConditionalDelimiterAsync(cancellationToken);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100576 if (await Reader.PeekAsync(cancellationToken) == TJSONProtocolConstants.Quote[0])
577 {
578 var arr = await ReadJsonStringAsync(true, cancellationToken);
579 var dub = double.Parse(Utf8Encoding.GetString(arr, 0, arr.Length), CultureInfo.InvariantCulture);
580
581 if (!Context.EscapeNumbers() && !double.IsNaN(dub) && !double.IsInfinity(dub))
582 {
583 // Throw exception -- we should not be in a string in this case
584 throw new TProtocolException(TProtocolException.INVALID_DATA, "Numeric data unexpectedly quoted");
585 }
586
587 return dub;
588 }
589
590 if (Context.EscapeNumbers())
591 {
592 // This will throw - we should have had a quote if escapeNum == true
593 await ReadJsonSyntaxCharAsync(TJSONProtocolConstants.Quote, cancellationToken);
594 }
595
596 try
597 {
598 return double.Parse(await ReadJsonNumericCharsAsync(cancellationToken), CultureInfo.InvariantCulture);
599 }
600 catch (FormatException)
601 {
602 throw new TProtocolException(TProtocolException.INVALID_DATA, "Bad data encounted in numeric data");
603 }
604 }
605
606 /// <summary>
607 /// Read in a JSON string containing base-64 encoded data and decode it.
608 /// </summary>
Jens Geyer5a17b132019-05-26 15:53:37 +0200609 private async ValueTask<byte[]> ReadJsonBase64Async(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100610 {
611 var b = await ReadJsonStringAsync(false, cancellationToken);
612 var len = b.Length;
613 var off = 0;
614 var size = 0;
615
616 // reduce len to ignore fill bytes
617 while ((len > 0) && (b[len - 1] == '='))
618 {
619 --len;
620 }
621
622 // read & decode full byte triplets = 4 source bytes
623 while (len > 4)
624 {
625 // Decode 4 bytes at a time
626 TBase64Utils.Decode(b, off, 4, b, size); // NB: decoded in place
627 off += 4;
628 len -= 4;
629 size += 3;
630 }
631
632 // Don't decode if we hit the end or got a single leftover byte (invalid
633 // base64 but legal for skip of regular string exType)
634 if (len > 1)
635 {
636 // Decode remainder
637 TBase64Utils.Decode(b, off, len, b, size); // NB: decoded in place
638 size += len - 1;
639 }
640
641 // Sadly we must copy the byte[] (any way around this?)
642 var result = new byte[size];
643 Array.Copy(b, 0, result, 0, size);
644 return result;
645 }
646
647 private async Task ReadJsonObjectStartAsync(CancellationToken cancellationToken)
648 {
Jens Geyer2ff952b2019-04-13 19:46:54 +0200649 await Context.ReadConditionalDelimiterAsync(cancellationToken);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100650 await ReadJsonSyntaxCharAsync(TJSONProtocolConstants.LeftBrace, cancellationToken);
651 PushContext(new JSONPairContext(this));
652 }
653
654 private async Task ReadJsonObjectEndAsync(CancellationToken cancellationToken)
655 {
656 await ReadJsonSyntaxCharAsync(TJSONProtocolConstants.RightBrace, cancellationToken);
657 PopContext();
658 }
659
660 private async Task ReadJsonArrayStartAsync(CancellationToken cancellationToken)
661 {
Jens Geyer2ff952b2019-04-13 19:46:54 +0200662 await Context.ReadConditionalDelimiterAsync(cancellationToken);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100663 await ReadJsonSyntaxCharAsync(TJSONProtocolConstants.LeftBracket, cancellationToken);
664 PushContext(new JSONListContext(this));
665 }
666
667 private async Task ReadJsonArrayEndAsync(CancellationToken cancellationToken)
668 {
669 await ReadJsonSyntaxCharAsync(TJSONProtocolConstants.RightBracket, cancellationToken);
670 PopContext();
671 }
672
Jens Geyer5a17b132019-05-26 15:53:37 +0200673 public override async ValueTask<TMessage> ReadMessageBeginAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100674 {
675 var message = new TMessage();
Paulo Nevesf049ff32020-02-05 11:58:18 +0100676
Jens Geyer0d128322021-02-25 09:42:52 +0100677 ResetContext();
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100678 await ReadJsonArrayStartAsync(cancellationToken);
679 if (await ReadJsonIntegerAsync(cancellationToken) != Version)
680 {
681 throw new TProtocolException(TProtocolException.BAD_VERSION, "Message contained bad version.");
682 }
683
684 var buf = await ReadJsonStringAsync(false, cancellationToken);
685 message.Name = Utf8Encoding.GetString(buf, 0, buf.Length);
686 message.Type = (TMessageType) await ReadJsonIntegerAsync(cancellationToken);
687 message.SeqID = (int) await ReadJsonIntegerAsync(cancellationToken);
688 return message;
689 }
690
691 public override async Task ReadMessageEndAsync(CancellationToken cancellationToken)
692 {
693 await ReadJsonArrayEndAsync(cancellationToken);
694 }
695
Jens Geyer5a17b132019-05-26 15:53:37 +0200696 public override async ValueTask<TStruct> ReadStructBeginAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100697 {
698 await ReadJsonObjectStartAsync(cancellationToken);
Jens Geyerdce22992020-05-16 23:02:27 +0200699
700 return AnonymousStruct;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100701 }
702
703 public override async Task ReadStructEndAsync(CancellationToken cancellationToken)
704 {
705 await ReadJsonObjectEndAsync(cancellationToken);
706 }
707
Jens Geyer5a17b132019-05-26 15:53:37 +0200708 public override async ValueTask<TField> ReadFieldBeginAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100709 {
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100710 var ch = await Reader.PeekAsync(cancellationToken);
711 if (ch == TJSONProtocolConstants.RightBrace[0])
712 {
Jens Geyerdce22992020-05-16 23:02:27 +0200713 return StopField;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100714 }
Jens Geyerdce22992020-05-16 23:02:27 +0200715
716 var field = new TField()
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100717 {
Jens Geyerdce22992020-05-16 23:02:27 +0200718 ID = (short)await ReadJsonIntegerAsync(cancellationToken)
719 };
720
721 await ReadJsonObjectStartAsync(cancellationToken);
722 field.Type = TJSONProtocolHelper.GetTypeIdForTypeName(await ReadJsonStringAsync(false, cancellationToken));
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100723 return field;
724 }
725
726 public override async Task ReadFieldEndAsync(CancellationToken cancellationToken)
727 {
728 await ReadJsonObjectEndAsync(cancellationToken);
729 }
730
Jens Geyer5a17b132019-05-26 15:53:37 +0200731 public override async ValueTask<TMap> ReadMapBeginAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100732 {
733 var map = new TMap();
734 await ReadJsonArrayStartAsync(cancellationToken);
735 map.KeyType = TJSONProtocolHelper.GetTypeIdForTypeName(await ReadJsonStringAsync(false, cancellationToken));
736 map.ValueType = TJSONProtocolHelper.GetTypeIdForTypeName(await ReadJsonStringAsync(false, cancellationToken));
737 map.Count = (int) await ReadJsonIntegerAsync(cancellationToken);
Jens Geyer50806452019-11-23 01:55:58 +0100738 CheckReadBytesAvailable(map);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100739 await ReadJsonObjectStartAsync(cancellationToken);
740 return map;
741 }
742
743 public override async Task ReadMapEndAsync(CancellationToken cancellationToken)
744 {
745 await ReadJsonObjectEndAsync(cancellationToken);
746 await ReadJsonArrayEndAsync(cancellationToken);
747 }
748
Jens Geyer5a17b132019-05-26 15:53:37 +0200749 public override async ValueTask<TList> ReadListBeginAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100750 {
751 var list = new TList();
752 await ReadJsonArrayStartAsync(cancellationToken);
753 list.ElementType = TJSONProtocolHelper.GetTypeIdForTypeName(await ReadJsonStringAsync(false, cancellationToken));
754 list.Count = (int) await ReadJsonIntegerAsync(cancellationToken);
Jens Geyer50806452019-11-23 01:55:58 +0100755 CheckReadBytesAvailable(list);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100756 return list;
757 }
758
759 public override async Task ReadListEndAsync(CancellationToken cancellationToken)
760 {
761 await ReadJsonArrayEndAsync(cancellationToken);
762 }
763
Jens Geyer5a17b132019-05-26 15:53:37 +0200764 public override async ValueTask<TSet> ReadSetBeginAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100765 {
766 var set = new TSet();
767 await ReadJsonArrayStartAsync(cancellationToken);
768 set.ElementType = TJSONProtocolHelper.GetTypeIdForTypeName(await ReadJsonStringAsync(false, cancellationToken));
769 set.Count = (int) await ReadJsonIntegerAsync(cancellationToken);
Jens Geyer50806452019-11-23 01:55:58 +0100770 CheckReadBytesAvailable(set);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100771 return set;
772 }
773
774 public override async Task ReadSetEndAsync(CancellationToken cancellationToken)
775 {
776 await ReadJsonArrayEndAsync(cancellationToken);
777 }
778
Jens Geyer5a17b132019-05-26 15:53:37 +0200779 public override async ValueTask<bool> ReadBoolAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100780 {
781 return await ReadJsonIntegerAsync(cancellationToken) != 0;
782 }
783
Jens Geyer5a17b132019-05-26 15:53:37 +0200784 public override async ValueTask<sbyte> ReadByteAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100785 {
786 return (sbyte) await ReadJsonIntegerAsync(cancellationToken);
787 }
788
Jens Geyer5a17b132019-05-26 15:53:37 +0200789 public override async ValueTask<short> ReadI16Async(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100790 {
791 return (short) await ReadJsonIntegerAsync(cancellationToken);
792 }
793
Jens Geyer5a17b132019-05-26 15:53:37 +0200794 public override async ValueTask<int> ReadI32Async(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100795 {
796 return (int) await ReadJsonIntegerAsync(cancellationToken);
797 }
798
Jens Geyer5a17b132019-05-26 15:53:37 +0200799 public override async ValueTask<long> ReadI64Async(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100800 {
801 return await ReadJsonIntegerAsync(cancellationToken);
802 }
803
Jens Geyer5a17b132019-05-26 15:53:37 +0200804 public override async ValueTask<double> ReadDoubleAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100805 {
806 return await ReadJsonDoubleAsync(cancellationToken);
807 }
808
Jens Geyer5a17b132019-05-26 15:53:37 +0200809 public override async ValueTask<string> ReadStringAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100810 {
811 var buf = await ReadJsonStringAsync(false, cancellationToken);
812 return Utf8Encoding.GetString(buf, 0, buf.Length);
813 }
814
Jens Geyer5a17b132019-05-26 15:53:37 +0200815 public override async ValueTask<byte[]> ReadBinaryAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100816 {
817 return await ReadJsonBase64Async(cancellationToken);
818 }
819
Jens Geyer50806452019-11-23 01:55:58 +0100820 // Return the minimum number of bytes a type will consume on the wire
821 public override int GetMinSerializedSize(TType type)
822 {
823 switch (type)
824 {
825 case TType.Stop: return 0;
826 case TType.Void: return 0;
827 case TType.Bool: return 1; // written as int
828 case TType.Byte: return 1;
829 case TType.Double: return 1;
830 case TType.I16: return 1;
831 case TType.I32: return 1;
832 case TType.I64: return 1;
833 case TType.String: return 2; // empty string
834 case TType.Struct: return 2; // empty struct
835 case TType.Map: return 2; // empty map
836 case TType.Set: return 2; // empty set
837 case TType.List: return 2; // empty list
Jens Geyer0d128322021-02-25 09:42:52 +0100838 default: throw new TProtocolException(TProtocolException.NOT_IMPLEMENTED, "unrecognized type code");
Jens Geyer50806452019-11-23 01:55:58 +0100839 }
840 }
841
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100842 /// <summary>
843 /// Factory for JSON protocol objects
844 /// </summary>
Jens Geyer421444f2019-03-20 22:13:25 +0100845 public class Factory : TProtocolFactory
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100846 {
Jens Geyer421444f2019-03-20 22:13:25 +0100847 public override TProtocol GetProtocol(TTransport trans)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100848 {
849 return new TJsonProtocol(trans);
850 }
851 }
852
853 /// <summary>
854 /// Base class for tracking JSON contexts that may require
855 /// inserting/Reading additional JSON syntax characters
856 /// This base context does nothing.
857 /// </summary>
858 protected class JSONBaseContext
859 {
860 protected TJsonProtocol Proto;
861
862 public JSONBaseContext(TJsonProtocol proto)
863 {
864 Proto = proto;
865 }
866
Jens Geyerdce22992020-05-16 23:02:27 +0200867 public virtual Task WriteConditionalDelimiterAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100868 {
Jens Geyerdce22992020-05-16 23:02:27 +0200869 cancellationToken.ThrowIfCancellationRequested();
870 return Task.CompletedTask;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100871 }
872
Jens Geyerdce22992020-05-16 23:02:27 +0200873 public virtual Task ReadConditionalDelimiterAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100874 {
Jens Geyerdce22992020-05-16 23:02:27 +0200875 cancellationToken.ThrowIfCancellationRequested();
876 return Task.CompletedTask;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100877 }
878
879 public virtual bool EscapeNumbers()
880 {
881 return false;
882 }
883 }
884
885 /// <summary>
886 /// Context for JSON lists. Will insert/Read commas before each item except
887 /// for the first one
888 /// </summary>
889 protected class JSONListContext : JSONBaseContext
890 {
891 private bool _first = true;
892
893 public JSONListContext(TJsonProtocol protocol)
894 : base(protocol)
895 {
896 }
897
Jens Geyer2ff952b2019-04-13 19:46:54 +0200898 public override async Task WriteConditionalDelimiterAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100899 {
900 if (_first)
901 {
902 _first = false;
903 }
904 else
905 {
906 await Proto.Trans.WriteAsync(TJSONProtocolConstants.Comma, cancellationToken);
907 }
908 }
909
Jens Geyer2ff952b2019-04-13 19:46:54 +0200910 public override async Task ReadConditionalDelimiterAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100911 {
912 if (_first)
913 {
914 _first = false;
915 }
916 else
917 {
918 await Proto.ReadJsonSyntaxCharAsync(TJSONProtocolConstants.Comma, cancellationToken);
919 }
920 }
921 }
922
923 /// <summary>
924 /// Context for JSON records. Will insert/Read colons before the value portion
925 /// of each record pair, and commas before each key except the first. In
926 /// addition, will indicate that numbers in the key position need to be
927 /// escaped in quotes (since JSON keys must be strings).
928 /// </summary>
929 // ReSharper disable once InconsistentNaming
930 protected class JSONPairContext : JSONBaseContext
931 {
932 private bool _colon = true;
933
934 private bool _first = true;
935
936 public JSONPairContext(TJsonProtocol proto)
937 : base(proto)
938 {
939 }
940
Jens Geyer2ff952b2019-04-13 19:46:54 +0200941 public override async Task WriteConditionalDelimiterAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100942 {
943 if (_first)
944 {
945 _first = false;
946 _colon = true;
947 }
948 else
949 {
950 await Proto.Trans.WriteAsync(_colon ? TJSONProtocolConstants.Colon : TJSONProtocolConstants.Comma, cancellationToken);
951 _colon = !_colon;
952 }
953 }
954
Jens Geyer2ff952b2019-04-13 19:46:54 +0200955 public override async Task ReadConditionalDelimiterAsync(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.ReadJsonSyntaxCharAsync(_colon ? TJSONProtocolConstants.Colon : TJSONProtocolConstants.Comma, cancellationToken);
965 _colon = !_colon;
966 }
967 }
968
969 public override bool EscapeNumbers()
970 {
971 return _colon;
972 }
973 }
974
975 /// <summary>
976 /// Holds up to one byte from the transport
977 /// </summary>
978 protected class LookaheadReader
979 {
980 private readonly byte[] _data = new byte[1];
981
982 private bool _hasData;
983 protected TJsonProtocol Proto;
984
985 public LookaheadReader(TJsonProtocol proto)
986 {
987 Proto = proto;
988 }
989
990 /// <summary>
991 /// Return and consume the next byte to be Read, either taking it from the
992 /// data buffer if present or getting it from the transport otherwise.
993 /// </summary>
Jens Geyer5a17b132019-05-26 15:53:37 +0200994 public async ValueTask<byte> ReadAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100995 {
Jens Geyerdce22992020-05-16 23:02:27 +0200996 cancellationToken.ThrowIfCancellationRequested();
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100997
998 if (_hasData)
999 {
1000 _hasData = false;
1001 }
1002 else
1003 {
1004 // find more easy way to avoid exception on reading primitive types
1005 await Proto.Trans.ReadAllAsync(_data, 0, 1, cancellationToken);
1006 }
1007 return _data[0];
1008 }
1009
1010 /// <summary>
1011 /// Return the next byte to be Read without consuming, filling the data
1012 /// buffer if it has not been filled alReady.
1013 /// </summary>
Jens Geyer5a17b132019-05-26 15:53:37 +02001014 public async ValueTask<byte> PeekAsync(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +01001015 {
Jens Geyerdce22992020-05-16 23:02:27 +02001016 cancellationToken.ThrowIfCancellationRequested();
Jens Geyeraa0c8b32019-01-28 23:27:45 +01001017
1018 if (!_hasData)
1019 {
1020 // find more easy way to avoid exception on reading primitive types
1021 await Proto.Trans.ReadAllAsync(_data, 0, 1, cancellationToken);
Jens Geyer5a17b132019-05-26 15:53:37 +02001022 _hasData = true;
Jens Geyeraa0c8b32019-01-28 23:27:45 +01001023 }
Jens Geyeraa0c8b32019-01-28 23:27:45 +01001024 return _data[0];
1025 }
1026 }
1027 }
1028}