1 // 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 18 using System; 19 using System.Collections.Generic; 20 using System.Globalization; 21 using System.IO; 22 using System.Linq; 23 using System.Text; 24 using System.Threading; 25 using System.Threading.Tasks; 26 using Thrift.Protocol.Entities; 27 using Thrift.Protocol.Utilities; 28 using Thrift.Transport; 29 30 31 namespace 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> TJsonProtocol(TTransport trans)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> PushContext(JSONBaseContext c)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> PopContext()82 protected void PopContext() 83 { 84 Context = ContextStack.Pop(); 85 } 86 87 /// <summary> 88 /// 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> ResetContext()92 private void ResetContext() 93 { 94 ContextStack.Clear(); 95 Context = new JSONBaseContext(this); 96 } 97 /// <summary> 98 /// 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> ReadJsonSyntaxCharAsync(byte[] bytes, CancellationToken cancellationToken)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> WriteJsonStringAsync(byte[] bytes, CancellationToken cancellationToken)114 private async Task WriteJsonStringAsync(byte[] bytes, CancellationToken cancellationToken) 115 { 116 await Context.WriteConditionalDelimiterAsync(cancellationToken); 117 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 { 131 await Trans.WriteAsync(bytes, i, 1, cancellationToken); 132 } 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> WriteJsonIntegerAsync(long num, CancellationToken cancellationToken)162 private async Task WriteJsonIntegerAsync(long num, CancellationToken cancellationToken) 163 { 164 await Context.WriteConditionalDelimiterAsync(cancellationToken); 165 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> WriteJsonDoubleAsync(double num, CancellationToken cancellationToken)186 private async Task WriteJsonDoubleAsync(double num, CancellationToken cancellationToken) 187 { 188 await Context.WriteConditionalDelimiterAsync(cancellationToken); 189 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> WriteJsonBase64Async(byte[] bytes, CancellationToken cancellationToken)226 private async Task WriteJsonBase64Async(byte[] bytes, CancellationToken cancellationToken) 227 { 228 await Context.WriteConditionalDelimiterAsync(cancellationToken); 229 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 WriteJsonObjectStartAsync(CancellationToken cancellationToken)253 private async Task WriteJsonObjectStartAsync(CancellationToken cancellationToken) 254 { 255 await Context.WriteConditionalDelimiterAsync(cancellationToken); 256 await Trans.WriteAsync(TJSONProtocolConstants.LeftBrace, cancellationToken); 257 PushContext(new JSONPairContext(this)); 258 } 259 WriteJsonObjectEndAsync(CancellationToken cancellationToken)260 private async Task WriteJsonObjectEndAsync(CancellationToken cancellationToken) 261 { 262 PopContext(); 263 await Trans.WriteAsync(TJSONProtocolConstants.RightBrace, cancellationToken); 264 } 265 WriteJsonArrayStartAsync(CancellationToken cancellationToken)266 private async Task WriteJsonArrayStartAsync(CancellationToken cancellationToken) 267 { 268 await Context.WriteConditionalDelimiterAsync(cancellationToken); 269 await Trans.WriteAsync(TJSONProtocolConstants.LeftBracket, cancellationToken); 270 PushContext(new JSONListContext(this)); 271 } 272 WriteJsonArrayEndAsync(CancellationToken cancellationToken)273 private async Task WriteJsonArrayEndAsync(CancellationToken cancellationToken) 274 { 275 PopContext(); 276 await Trans.WriteAsync(TJSONProtocolConstants.RightBracket, cancellationToken); 277 } 278 WriteMessageBeginAsync(TMessage message, CancellationToken cancellationToken)279 public override async Task WriteMessageBeginAsync(TMessage message, CancellationToken cancellationToken) 280 { 281 ResetContext(); 282 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 WriteMessageEndAsync(CancellationToken cancellationToken)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 WriteStructEndAsync(CancellationToken cancellationToken)302 public override async Task WriteStructEndAsync(CancellationToken cancellationToken) 303 { 304 await WriteJsonObjectEndAsync(cancellationToken); 305 } 306 WriteFieldBeginAsync(TField field, CancellationToken cancellationToken)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 WriteFieldEndAsync(CancellationToken cancellationToken)314 public override async Task WriteFieldEndAsync(CancellationToken cancellationToken) 315 { 316 await WriteJsonObjectEndAsync(cancellationToken); 317 } 318 WriteFieldStopAsync(CancellationToken cancellationToken)319 public override Task WriteFieldStopAsync(CancellationToken cancellationToken) 320 { 321 cancellationToken.ThrowIfCancellationRequested(); 322 return Task.CompletedTask; 323 } 324 WriteMapBeginAsync(TMap map, CancellationToken cancellationToken)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 WriteMapEndAsync(CancellationToken cancellationToken)334 public override async Task WriteMapEndAsync(CancellationToken cancellationToken) 335 { 336 await WriteJsonObjectEndAsync(cancellationToken); 337 await WriteJsonArrayEndAsync(cancellationToken); 338 } 339 WriteListBeginAsync(TList list, CancellationToken cancellationToken)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 WriteListEndAsync(CancellationToken cancellationToken)347 public override async Task WriteListEndAsync(CancellationToken cancellationToken) 348 { 349 await WriteJsonArrayEndAsync(cancellationToken); 350 } 351 WriteSetBeginAsync(TSet set, CancellationToken cancellationToken)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 WriteSetEndAsync(CancellationToken cancellationToken)359 public override async Task WriteSetEndAsync(CancellationToken cancellationToken) 360 { 361 await WriteJsonArrayEndAsync(cancellationToken); 362 } 363 WriteBoolAsync(bool b, CancellationToken cancellationToken)364 public override async Task WriteBoolAsync(bool b, CancellationToken cancellationToken) 365 { 366 await WriteJsonIntegerAsync(b ? 1 : 0, cancellationToken); 367 } 368 WriteByteAsync(sbyte b, CancellationToken cancellationToken)369 public override async Task WriteByteAsync(sbyte b, CancellationToken cancellationToken) 370 { 371 await WriteJsonIntegerAsync(b, cancellationToken); 372 } 373 WriteI16Async(short i16, CancellationToken cancellationToken)374 public override async Task WriteI16Async(short i16, CancellationToken cancellationToken) 375 { 376 await WriteJsonIntegerAsync(i16, cancellationToken); 377 } 378 WriteI32Async(int i32, CancellationToken cancellationToken)379 public override async Task WriteI32Async(int i32, CancellationToken cancellationToken) 380 { 381 await WriteJsonIntegerAsync(i32, cancellationToken); 382 } 383 WriteI64Async(long i64, CancellationToken cancellationToken)384 public override async Task WriteI64Async(long i64, CancellationToken cancellationToken) 385 { 386 await WriteJsonIntegerAsync(i64, cancellationToken); 387 } 388 WriteDoubleAsync(double d, CancellationToken cancellationToken)389 public override async Task WriteDoubleAsync(double d, CancellationToken cancellationToken) 390 { 391 await WriteJsonDoubleAsync(d, cancellationToken); 392 } 393 WriteStringAsync(string s, CancellationToken cancellationToken)394 public override async Task WriteStringAsync(string s, CancellationToken cancellationToken) 395 { 396 var b = Utf8Encoding.GetBytes(s); 397 await WriteJsonStringAsync(b, cancellationToken); 398 } 399 WriteBinaryAsync(byte[] bytes, CancellationToken cancellationToken)400 public override async Task WriteBinaryAsync(byte[] bytes, CancellationToken cancellationToken) 401 { 402 await WriteJsonBase64Async(bytes, cancellationToken); 403 } WriteUuidAsync(Guid uuid, CancellationToken cancellationToken = default)404 public override async Task WriteUuidAsync(Guid uuid, CancellationToken cancellationToken = default) 405 { 406 await WriteStringAsync(uuid.ToString("D"), cancellationToken); // no curly braces 407 } 408 409 /// <summary> 410 /// Read in a JSON string, unescaping as appropriate.. Skip Reading from the 411 /// context if skipContext is true. 412 /// </summary> ReadJsonStringAsync(bool skipContext, CancellationToken cancellationToken)413 private async ValueTask<byte[]> ReadJsonStringAsync(bool skipContext, CancellationToken cancellationToken) 414 { 415 using (var buffer = new MemoryStream()) 416 { 417 var codeunits = new List<char>(); 418 419 420 if (!skipContext) 421 { 422 await Context.ReadConditionalDelimiterAsync(cancellationToken); 423 } 424 425 await ReadJsonSyntaxCharAsync(TJSONProtocolConstants.Quote, cancellationToken); 426 427 while (true) 428 { 429 var ch = await Reader.ReadAsync(cancellationToken); 430 if (ch == TJSONProtocolConstants.Quote[0]) 431 { 432 break; 433 } 434 435 // escaped? 436 if (ch != TJSONProtocolConstants.EscSequences[0]) 437 { 438 #if NETSTANDARD2_0 439 await buffer.WriteAsync(new[] {ch}, 0, 1, cancellationToken); 440 #else 441 var wbuf = new[] { ch }; 442 await buffer.WriteAsync(wbuf.AsMemory(0, 1), cancellationToken); 443 #endif 444 continue; 445 } 446 447 // distinguish between \uXXXX and \? 448 ch = await Reader.ReadAsync(cancellationToken); 449 if (ch != TJSONProtocolConstants.EscSequences[1]) // control chars like \n 450 { 451 var off = Array.IndexOf(TJSONProtocolConstants.EscapeChars, (char) ch); 452 if (off == -1) 453 { 454 throw new TProtocolException(TProtocolException.INVALID_DATA, "Expected control char"); 455 } 456 ch = TJSONProtocolConstants.EscapeCharValues[off]; 457 #if NETSTANDARD2_0 458 await buffer.WriteAsync(new[] {ch}, 0, 1, cancellationToken); 459 #else 460 var wbuf = new[] { ch }; 461 await buffer.WriteAsync( wbuf.AsMemory(0, 1), cancellationToken); 462 #endif 463 continue; 464 } 465 466 // it's \uXXXX 467 await Trans.ReadAllAsync(_tempBuffer, 0, 4, cancellationToken); 468 469 var wch = (short) ((TJSONProtocolHelper.ToHexVal(_tempBuffer[0]) << 12) + 470 (TJSONProtocolHelper.ToHexVal(_tempBuffer[1]) << 8) + 471 (TJSONProtocolHelper.ToHexVal(_tempBuffer[2]) << 4) + 472 TJSONProtocolHelper.ToHexVal(_tempBuffer[3])); 473 474 if (char.IsHighSurrogate((char) wch)) 475 { 476 if (codeunits.Count > 0) 477 { 478 throw new TProtocolException(TProtocolException.INVALID_DATA, "Expected low surrogate char"); 479 } 480 codeunits.Add((char) wch); 481 } 482 else if (char.IsLowSurrogate((char) wch)) 483 { 484 if (codeunits.Count == 0) 485 { 486 throw new TProtocolException(TProtocolException.INVALID_DATA, "Expected high surrogate char"); 487 } 488 489 codeunits.Add((char) wch); 490 var tmp = Utf8Encoding.GetBytes(codeunits.ToArray()); 491 #if NETSTANDARD2_0 492 await buffer.WriteAsync(tmp, 0, tmp.Length, cancellationToken); 493 #else 494 await buffer.WriteAsync(tmp.AsMemory(0, tmp.Length), cancellationToken); 495 #endif 496 codeunits.Clear(); 497 } 498 else 499 { 500 var tmp = Utf8Encoding.GetBytes(new[] { (char)wch }); 501 #if NETSTANDARD2_0 502 await buffer.WriteAsync(tmp, 0, tmp.Length, cancellationToken); 503 #else 504 await buffer.WriteAsync(tmp.AsMemory( 0, tmp.Length), cancellationToken); 505 #endif 506 } 507 } 508 509 if (codeunits.Count > 0) 510 { 511 throw new TProtocolException(TProtocolException.INVALID_DATA, "Expected low surrogate char"); 512 } 513 514 return buffer.ToArray(); 515 } 516 } 517 518 /// <summary> 519 /// Read in a sequence of characters that are all valid in JSON numbers. Does 520 /// not do a complete regex check to validate that this is actually a number. 521 /// </summary> ReadJsonNumericCharsAsync(CancellationToken cancellationToken)522 private async ValueTask<string> ReadJsonNumericCharsAsync(CancellationToken cancellationToken) 523 { 524 var strbld = new StringBuilder(); 525 while (true) 526 { 527 //TODO: workaround for primitive types with TJsonProtocol, think - how to rewrite into more easy form without exceptions 528 try 529 { 530 var ch = await Reader.PeekAsync(cancellationToken); 531 if (!TJSONProtocolHelper.IsJsonNumeric(ch)) 532 { 533 break; 534 } 535 var c = (char)await Reader.ReadAsync(cancellationToken); 536 strbld.Append(c); 537 } 538 catch (TTransportException) 539 { 540 break; 541 } 542 } 543 return strbld.ToString(); 544 } 545 546 /// <summary> 547 /// Read in a JSON number. If the context dictates, Read in enclosing quotes. 548 /// </summary> ReadJsonIntegerAsync(CancellationToken cancellationToken)549 private async ValueTask<long> ReadJsonIntegerAsync(CancellationToken cancellationToken) 550 { 551 await Context.ReadConditionalDelimiterAsync(cancellationToken); 552 if (Context.EscapeNumbers()) 553 { 554 await ReadJsonSyntaxCharAsync(TJSONProtocolConstants.Quote, cancellationToken); 555 } 556 557 var str = await ReadJsonNumericCharsAsync(cancellationToken); 558 if (Context.EscapeNumbers()) 559 { 560 await ReadJsonSyntaxCharAsync(TJSONProtocolConstants.Quote, cancellationToken); 561 } 562 563 try 564 { 565 return long.Parse(str); 566 } 567 catch (FormatException) 568 { 569 throw new TProtocolException(TProtocolException.INVALID_DATA, "Bad data encounted in numeric data"); 570 } 571 } 572 573 /// <summary> 574 /// Read in a JSON double value. Throw if the value is not wrapped in quotes 575 /// when expected or if wrapped in quotes when not expected. 576 /// </summary> ReadJsonDoubleAsync(CancellationToken cancellationToken)577 private async ValueTask<double> ReadJsonDoubleAsync(CancellationToken cancellationToken) 578 { 579 await Context.ReadConditionalDelimiterAsync(cancellationToken); 580 if (await Reader.PeekAsync(cancellationToken) == TJSONProtocolConstants.Quote[0]) 581 { 582 var arr = await ReadJsonStringAsync(true, cancellationToken); 583 var dub = double.Parse(Utf8Encoding.GetString(arr, 0, arr.Length), CultureInfo.InvariantCulture); 584 585 if (!Context.EscapeNumbers() && !double.IsNaN(dub) && !double.IsInfinity(dub)) 586 { 587 // Throw exception -- we should not be in a string in this case 588 throw new TProtocolException(TProtocolException.INVALID_DATA, "Numeric data unexpectedly quoted"); 589 } 590 591 return dub; 592 } 593 594 if (Context.EscapeNumbers()) 595 { 596 // This will throw - we should have had a quote if escapeNum == true 597 await ReadJsonSyntaxCharAsync(TJSONProtocolConstants.Quote, cancellationToken); 598 } 599 600 try 601 { 602 return double.Parse(await ReadJsonNumericCharsAsync(cancellationToken), CultureInfo.InvariantCulture); 603 } 604 catch (FormatException) 605 { 606 throw new TProtocolException(TProtocolException.INVALID_DATA, "Bad data encounted in numeric data"); 607 } 608 } 609 610 /// <summary> 611 /// Read in a JSON string containing base-64 encoded data and decode it. 612 /// </summary> ReadJsonBase64Async(CancellationToken cancellationToken)613 private async ValueTask<byte[]> ReadJsonBase64Async(CancellationToken cancellationToken) 614 { 615 var b = await ReadJsonStringAsync(false, cancellationToken); 616 var len = b.Length; 617 var off = 0; 618 var size = 0; 619 620 // reduce len to ignore fill bytes 621 while ((len > 0) && (b[len - 1] == '=')) 622 { 623 --len; 624 } 625 626 // read & decode full byte triplets = 4 source bytes 627 while (len > 4) 628 { 629 // Decode 4 bytes at a time 630 TBase64Utils.Decode(b, off, 4, b, size); // NB: decoded in place 631 off += 4; 632 len -= 4; 633 size += 3; 634 } 635 636 // Don't decode if we hit the end or got a single leftover byte (invalid 637 // base64 but legal for skip of regular string exType) 638 if (len > 1) 639 { 640 // Decode remainder 641 TBase64Utils.Decode(b, off, len, b, size); // NB: decoded in place 642 size += len - 1; 643 } 644 645 // Sadly we must copy the byte[] (any way around this?) 646 var result = new byte[size]; 647 Array.Copy(b, 0, result, 0, size); 648 return result; 649 } 650 ReadJsonObjectStartAsync(CancellationToken cancellationToken)651 private async Task ReadJsonObjectStartAsync(CancellationToken cancellationToken) 652 { 653 await Context.ReadConditionalDelimiterAsync(cancellationToken); 654 await ReadJsonSyntaxCharAsync(TJSONProtocolConstants.LeftBrace, cancellationToken); 655 PushContext(new JSONPairContext(this)); 656 } 657 ReadJsonObjectEndAsync(CancellationToken cancellationToken)658 private async Task ReadJsonObjectEndAsync(CancellationToken cancellationToken) 659 { 660 await ReadJsonSyntaxCharAsync(TJSONProtocolConstants.RightBrace, cancellationToken); 661 PopContext(); 662 } 663 ReadJsonArrayStartAsync(CancellationToken cancellationToken)664 private async Task ReadJsonArrayStartAsync(CancellationToken cancellationToken) 665 { 666 await Context.ReadConditionalDelimiterAsync(cancellationToken); 667 await ReadJsonSyntaxCharAsync(TJSONProtocolConstants.LeftBracket, cancellationToken); 668 PushContext(new JSONListContext(this)); 669 } 670 ReadJsonArrayEndAsync(CancellationToken cancellationToken)671 private async Task ReadJsonArrayEndAsync(CancellationToken cancellationToken) 672 { 673 await ReadJsonSyntaxCharAsync(TJSONProtocolConstants.RightBracket, cancellationToken); 674 PopContext(); 675 } 676 ReadMessageBeginAsync(CancellationToken cancellationToken)677 public override async ValueTask<TMessage> ReadMessageBeginAsync(CancellationToken cancellationToken) 678 { 679 var message = new TMessage(); 680 681 ResetContext(); 682 await ReadJsonArrayStartAsync(cancellationToken); 683 if (await ReadJsonIntegerAsync(cancellationToken) != Version) 684 { 685 throw new TProtocolException(TProtocolException.BAD_VERSION, "Message contained bad version."); 686 } 687 688 var buf = await ReadJsonStringAsync(false, cancellationToken); 689 message.Name = Utf8Encoding.GetString(buf, 0, buf.Length); 690 message.Type = (TMessageType) await ReadJsonIntegerAsync(cancellationToken); 691 message.SeqID = (int) await ReadJsonIntegerAsync(cancellationToken); 692 return message; 693 } 694 ReadMessageEndAsync(CancellationToken cancellationToken)695 public override async Task ReadMessageEndAsync(CancellationToken cancellationToken) 696 { 697 cancellationToken.ThrowIfCancellationRequested(); 698 await ReadJsonArrayEndAsync(cancellationToken); 699 Transport.ResetConsumedMessageSize(); 700 } 701 ReadStructBeginAsync(CancellationToken cancellationToken)702 public override async ValueTask<TStruct> ReadStructBeginAsync(CancellationToken cancellationToken) 703 { 704 await ReadJsonObjectStartAsync(cancellationToken); 705 706 return AnonymousStruct; 707 } 708 ReadStructEndAsync(CancellationToken cancellationToken)709 public override async Task ReadStructEndAsync(CancellationToken cancellationToken) 710 { 711 await ReadJsonObjectEndAsync(cancellationToken); 712 } 713 ReadFieldBeginAsync(CancellationToken cancellationToken)714 public override async ValueTask<TField> ReadFieldBeginAsync(CancellationToken cancellationToken) 715 { 716 var ch = await Reader.PeekAsync(cancellationToken); 717 if (ch == TJSONProtocolConstants.RightBrace[0]) 718 { 719 return StopField; 720 } 721 722 var field = new TField() 723 { 724 ID = (short)await ReadJsonIntegerAsync(cancellationToken) 725 }; 726 727 await ReadJsonObjectStartAsync(cancellationToken); 728 field.Type = TJSONProtocolHelper.GetTypeIdForTypeName(await ReadJsonStringAsync(false, cancellationToken)); 729 return field; 730 } 731 ReadFieldEndAsync(CancellationToken cancellationToken)732 public override async Task ReadFieldEndAsync(CancellationToken cancellationToken) 733 { 734 await ReadJsonObjectEndAsync(cancellationToken); 735 } 736 ReadMapBeginAsync(CancellationToken cancellationToken)737 public override async ValueTask<TMap> ReadMapBeginAsync(CancellationToken cancellationToken) 738 { 739 var map = new TMap(); 740 await ReadJsonArrayStartAsync(cancellationToken); 741 map.KeyType = TJSONProtocolHelper.GetTypeIdForTypeName(await ReadJsonStringAsync(false, cancellationToken)); 742 map.ValueType = TJSONProtocolHelper.GetTypeIdForTypeName(await ReadJsonStringAsync(false, cancellationToken)); 743 map.Count = (int) await ReadJsonIntegerAsync(cancellationToken); 744 CheckReadBytesAvailable(map); 745 await ReadJsonObjectStartAsync(cancellationToken); 746 return map; 747 } 748 ReadMapEndAsync(CancellationToken cancellationToken)749 public override async Task ReadMapEndAsync(CancellationToken cancellationToken) 750 { 751 await ReadJsonObjectEndAsync(cancellationToken); 752 await ReadJsonArrayEndAsync(cancellationToken); 753 } 754 ReadListBeginAsync(CancellationToken cancellationToken)755 public override async ValueTask<TList> ReadListBeginAsync(CancellationToken cancellationToken) 756 { 757 var list = new TList(); 758 await ReadJsonArrayStartAsync(cancellationToken); 759 list.ElementType = TJSONProtocolHelper.GetTypeIdForTypeName(await ReadJsonStringAsync(false, cancellationToken)); 760 list.Count = (int) await ReadJsonIntegerAsync(cancellationToken); 761 CheckReadBytesAvailable(list); 762 return list; 763 } 764 ReadListEndAsync(CancellationToken cancellationToken)765 public override async Task ReadListEndAsync(CancellationToken cancellationToken) 766 { 767 await ReadJsonArrayEndAsync(cancellationToken); 768 } 769 ReadSetBeginAsync(CancellationToken cancellationToken)770 public override async ValueTask<TSet> ReadSetBeginAsync(CancellationToken cancellationToken) 771 { 772 var set = new TSet(); 773 await ReadJsonArrayStartAsync(cancellationToken); 774 set.ElementType = TJSONProtocolHelper.GetTypeIdForTypeName(await ReadJsonStringAsync(false, cancellationToken)); 775 set.Count = (int) await ReadJsonIntegerAsync(cancellationToken); 776 CheckReadBytesAvailable(set); 777 return set; 778 } 779 ReadSetEndAsync(CancellationToken cancellationToken)780 public override async Task ReadSetEndAsync(CancellationToken cancellationToken) 781 { 782 await ReadJsonArrayEndAsync(cancellationToken); 783 } 784 ReadBoolAsync(CancellationToken cancellationToken)785 public override async ValueTask<bool> ReadBoolAsync(CancellationToken cancellationToken) 786 { 787 return await ReadJsonIntegerAsync(cancellationToken) != 0; 788 } 789 ReadByteAsync(CancellationToken cancellationToken)790 public override async ValueTask<sbyte> ReadByteAsync(CancellationToken cancellationToken) 791 { 792 return (sbyte) await ReadJsonIntegerAsync(cancellationToken); 793 } 794 ReadI16Async(CancellationToken cancellationToken)795 public override async ValueTask<short> ReadI16Async(CancellationToken cancellationToken) 796 { 797 return (short) await ReadJsonIntegerAsync(cancellationToken); 798 } 799 ReadI32Async(CancellationToken cancellationToken)800 public override async ValueTask<int> ReadI32Async(CancellationToken cancellationToken) 801 { 802 return (int) await ReadJsonIntegerAsync(cancellationToken); 803 } 804 ReadI64Async(CancellationToken cancellationToken)805 public override async ValueTask<long> ReadI64Async(CancellationToken cancellationToken) 806 { 807 return await ReadJsonIntegerAsync(cancellationToken); 808 } 809 ReadDoubleAsync(CancellationToken cancellationToken)810 public override async ValueTask<double> ReadDoubleAsync(CancellationToken cancellationToken) 811 { 812 return await ReadJsonDoubleAsync(cancellationToken); 813 } 814 ReadStringAsync(CancellationToken cancellationToken)815 public override async ValueTask<string> ReadStringAsync(CancellationToken cancellationToken) 816 { 817 var buf = await ReadJsonStringAsync(false, cancellationToken); 818 return Utf8Encoding.GetString(buf, 0, buf.Length); 819 } 820 ReadBinaryAsync(CancellationToken cancellationToken)821 public override async ValueTask<byte[]> ReadBinaryAsync(CancellationToken cancellationToken) 822 { 823 return await ReadJsonBase64Async(cancellationToken); 824 } 825 ReadUuidAsync(CancellationToken cancellationToken = default)826 public override async ValueTask<Guid> ReadUuidAsync(CancellationToken cancellationToken = default) 827 { 828 return new Guid( await ReadStringAsync(cancellationToken)); 829 } 830 831 // Return the minimum number of bytes a type will consume on the wire GetMinSerializedSize(TType type)832 public override int GetMinSerializedSize(TType type) 833 { 834 switch (type) 835 { 836 case TType.Stop: return 0; 837 case TType.Void: return 0; 838 case TType.Bool: return 1; // written as int 839 case TType.Byte: return 1; 840 case TType.Double: return 1; 841 case TType.I16: return 1; 842 case TType.I32: return 1; 843 case TType.I64: return 1; 844 case TType.String: return 2; // empty string 845 case TType.Struct: return 2; // empty struct 846 case TType.Map: return 2; // empty map 847 case TType.Set: return 2; // empty set 848 case TType.List: return 2; // empty list 849 case TType.Uuid: return 36; // "E236974D-F0B0-4E05-8F29-0B455D41B1A1" 850 default: throw new TProtocolException(TProtocolException.NOT_IMPLEMENTED, "unrecognized type code"); 851 } 852 } 853 854 /// <summary> 855 /// Factory for JSON protocol objects 856 /// </summary> 857 public class Factory : TProtocolFactory 858 { GetProtocol(TTransport trans)859 public override TProtocol GetProtocol(TTransport trans) 860 { 861 return new TJsonProtocol(trans); 862 } 863 } 864 865 /// <summary> 866 /// Base class for tracking JSON contexts that may require 867 /// inserting/Reading additional JSON syntax characters 868 /// This base context does nothing. 869 /// </summary> 870 protected class JSONBaseContext 871 { 872 protected TJsonProtocol Proto; 873 JSONBaseContext(TJsonProtocol proto)874 public JSONBaseContext(TJsonProtocol proto) 875 { 876 Proto = proto; 877 } 878 WriteConditionalDelimiterAsync(CancellationToken cancellationToken)879 public virtual Task WriteConditionalDelimiterAsync(CancellationToken cancellationToken) 880 { 881 cancellationToken.ThrowIfCancellationRequested(); 882 return Task.CompletedTask; 883 } 884 ReadConditionalDelimiterAsync(CancellationToken cancellationToken)885 public virtual Task ReadConditionalDelimiterAsync(CancellationToken cancellationToken) 886 { 887 cancellationToken.ThrowIfCancellationRequested(); 888 return Task.CompletedTask; 889 } 890 EscapeNumbers()891 public virtual bool EscapeNumbers() 892 { 893 return false; 894 } 895 } 896 897 /// <summary> 898 /// Context for JSON lists. Will insert/Read commas before each item except 899 /// for the first one 900 /// </summary> 901 protected class JSONListContext : JSONBaseContext 902 { 903 private bool _first = true; 904 JSONListContext(TJsonProtocol protocol)905 public JSONListContext(TJsonProtocol protocol) 906 : base(protocol) 907 { 908 } 909 WriteConditionalDelimiterAsync(CancellationToken cancellationToken)910 public override async Task WriteConditionalDelimiterAsync(CancellationToken cancellationToken) 911 { 912 if (_first) 913 { 914 _first = false; 915 } 916 else 917 { 918 await Proto.Trans.WriteAsync(TJSONProtocolConstants.Comma, cancellationToken); 919 } 920 } 921 ReadConditionalDelimiterAsync(CancellationToken cancellationToken)922 public override async Task ReadConditionalDelimiterAsync(CancellationToken cancellationToken) 923 { 924 if (_first) 925 { 926 _first = false; 927 } 928 else 929 { 930 await Proto.ReadJsonSyntaxCharAsync(TJSONProtocolConstants.Comma, cancellationToken); 931 } 932 } 933 } 934 935 /// <summary> 936 /// Context for JSON records. Will insert/Read colons before the value portion 937 /// of each record pair, and commas before each key except the first. In 938 /// addition, will indicate that numbers in the key position need to be 939 /// escaped in quotes (since JSON keys must be strings). 940 /// </summary> 941 // ReSharper disable once InconsistentNaming 942 protected class JSONPairContext : JSONBaseContext 943 { 944 private bool _colon = true; 945 946 private bool _first = true; 947 JSONPairContext(TJsonProtocol proto)948 public JSONPairContext(TJsonProtocol proto) 949 : base(proto) 950 { 951 } 952 WriteConditionalDelimiterAsync(CancellationToken cancellationToken)953 public override async Task WriteConditionalDelimiterAsync(CancellationToken cancellationToken) 954 { 955 if (_first) 956 { 957 _first = false; 958 _colon = true; 959 } 960 else 961 { 962 await Proto.Trans.WriteAsync(_colon ? TJSONProtocolConstants.Colon : TJSONProtocolConstants.Comma, cancellationToken); 963 _colon = !_colon; 964 } 965 } 966 ReadConditionalDelimiterAsync(CancellationToken cancellationToken)967 public override async Task ReadConditionalDelimiterAsync(CancellationToken cancellationToken) 968 { 969 if (_first) 970 { 971 _first = false; 972 _colon = true; 973 } 974 else 975 { 976 await Proto.ReadJsonSyntaxCharAsync(_colon ? TJSONProtocolConstants.Colon : TJSONProtocolConstants.Comma, cancellationToken); 977 _colon = !_colon; 978 } 979 } 980 EscapeNumbers()981 public override bool EscapeNumbers() 982 { 983 return _colon; 984 } 985 } 986 987 /// <summary> 988 /// Holds up to one byte from the transport 989 /// </summary> 990 protected class LookaheadReader 991 { 992 private readonly byte[] _data = new byte[1]; 993 994 private bool _hasData; 995 protected TJsonProtocol Proto; 996 LookaheadReader(TJsonProtocol proto)997 public LookaheadReader(TJsonProtocol proto) 998 { 999 Proto = proto; 1000 } 1001 1002 /// <summary> 1003 /// Return and consume the next byte to be Read, either taking it from the 1004 /// data buffer if present or getting it from the transport otherwise. 1005 /// </summary> ReadAsync(CancellationToken cancellationToken)1006 public async ValueTask<byte> ReadAsync(CancellationToken cancellationToken) 1007 { 1008 cancellationToken.ThrowIfCancellationRequested(); 1009 1010 if (_hasData) 1011 { 1012 _hasData = false; 1013 } 1014 else 1015 { 1016 // find more easy way to avoid exception on reading primitive types 1017 await Proto.Trans.ReadAllAsync(_data, 0, 1, cancellationToken); 1018 } 1019 return _data[0]; 1020 } 1021 1022 /// <summary> 1023 /// Return the next byte to be Read without consuming, filling the data 1024 /// buffer if it has not been filled alReady. 1025 /// </summary> PeekAsync(CancellationToken cancellationToken)1026 public async ValueTask<byte> PeekAsync(CancellationToken cancellationToken) 1027 { 1028 cancellationToken.ThrowIfCancellationRequested(); 1029 1030 if (!_hasData) 1031 { 1032 // find more easy way to avoid exception on reading primitive types 1033 await Proto.Trans.ReadAllAsync(_data, 0, 1, cancellationToken); 1034 _hasData = true; 1035 } 1036 return _data[0]; 1037 } 1038 } 1039 } 1040 } 1041