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