1 /** 2 * Licensed to the Apache Software Foundation (ASF) under one 3 * or more contributor license agreements. See the NOTICE file 4 * distributed with this work for additional information 5 * regarding copyright ownership. The ASF licenses this file 6 * to you under the Apache License, Version 2.0 (the 7 * "License"); you may not use this file except in compliance 8 * with the License. You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, 13 * software distributed under the License is distributed on an 14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 * KIND, either express or implied. See the License for the 16 * specific language governing permissions and limitations 17 * under the License. 18 */ 19 20 using System; 21 using System.IO; 22 using System.Text; 23 using System.Collections.Generic; 24 25 using Thrift.Transport; 26 using System.Globalization; 27 28 namespace Thrift.Protocol 29 { 30 /// <summary> 31 /// JSON protocol implementation for thrift. 32 /// <para/> 33 /// This is a full-featured protocol supporting Write and Read. 34 /// <para/> 35 /// Please see the C++ class header for a detailed description of the 36 /// protocol's wire format. 37 /// <para/> 38 /// Adapted from the Java version. 39 /// </summary> 40 public class TJSONProtocol : TProtocol 41 { 42 /// <summary> 43 /// Factory for JSON protocol objects. 44 /// </summary> 45 public class Factory : TProtocolFactory 46 { GetProtocol(TTransport trans)47 public TProtocol GetProtocol(TTransport trans) 48 { 49 return new TJSONProtocol(trans); 50 } 51 } 52 53 private static byte[] COMMA = new byte[] { (byte)',' }; 54 private static byte[] COLON = new byte[] { (byte)':' }; 55 private static byte[] LBRACE = new byte[] { (byte)'{' }; 56 private static byte[] RBRACE = new byte[] { (byte)'}' }; 57 private static byte[] LBRACKET = new byte[] { (byte)'[' }; 58 private static byte[] RBRACKET = new byte[] { (byte)']' }; 59 private static byte[] QUOTE = new byte[] { (byte)'"' }; 60 private static byte[] BACKSLASH = new byte[] { (byte)'\\' }; 61 62 private byte[] ESCSEQ = new byte[] { (byte)'\\', (byte)'u', (byte)'0', (byte)'0' }; 63 64 private const long VERSION = 1; 65 private byte[] JSON_CHAR_TABLE = { 66 0, 0, 0, 0, 0, 0, 0, 0,(byte)'b',(byte)'t',(byte)'n', 0,(byte)'f',(byte)'r', 0, 0, 67 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 68 1, 1,(byte)'"', 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 69 }; 70 71 private char[] ESCAPE_CHARS = "\"\\/bfnrt".ToCharArray(); 72 73 private byte[] ESCAPE_CHAR_VALS = { 74 (byte)'"', (byte)'\\', (byte)'/', (byte)'\b', (byte)'\f', (byte)'\n', (byte)'\r', (byte)'\t', 75 }; 76 77 private const int DEF_STRING_SIZE = 16; 78 79 private static byte[] NAME_BOOL = new byte[] { (byte)'t', (byte)'f' }; 80 private static byte[] NAME_BYTE = new byte[] { (byte)'i', (byte)'8' }; 81 private static byte[] NAME_I16 = new byte[] { (byte)'i', (byte)'1', (byte)'6' }; 82 private static byte[] NAME_I32 = new byte[] { (byte)'i', (byte)'3', (byte)'2' }; 83 private static byte[] NAME_I64 = new byte[] { (byte)'i', (byte)'6', (byte)'4' }; 84 private static byte[] NAME_DOUBLE = new byte[] { (byte)'d', (byte)'b', (byte)'l' }; 85 private static byte[] NAME_STRUCT = new byte[] { (byte)'r', (byte)'e', (byte)'c' }; 86 private static byte[] NAME_STRING = new byte[] { (byte)'s', (byte)'t', (byte)'r' }; 87 private static byte[] NAME_MAP = new byte[] { (byte)'m', (byte)'a', (byte)'p' }; 88 private static byte[] NAME_LIST = new byte[] { (byte)'l', (byte)'s', (byte)'t' }; 89 private static byte[] NAME_SET = new byte[] { (byte)'s', (byte)'e', (byte)'t' }; 90 GetTypeNameForTypeID(TType typeID)91 private static byte[] GetTypeNameForTypeID(TType typeID) 92 { 93 switch (typeID) 94 { 95 case TType.Bool: 96 return NAME_BOOL; 97 case TType.Byte: 98 return NAME_BYTE; 99 case TType.I16: 100 return NAME_I16; 101 case TType.I32: 102 return NAME_I32; 103 case TType.I64: 104 return NAME_I64; 105 case TType.Double: 106 return NAME_DOUBLE; 107 case TType.String: 108 return NAME_STRING; 109 case TType.Struct: 110 return NAME_STRUCT; 111 case TType.Map: 112 return NAME_MAP; 113 case TType.Set: 114 return NAME_SET; 115 case TType.List: 116 return NAME_LIST; 117 default: 118 throw new TProtocolException(TProtocolException.NOT_IMPLEMENTED, 119 "Unrecognized type"); 120 } 121 } 122 GetTypeIDForTypeName(byte[] name)123 private static TType GetTypeIDForTypeName(byte[] name) 124 { 125 TType result = TType.Stop; 126 if (name.Length > 1) 127 { 128 switch (name[0]) 129 { 130 case (byte)'d': 131 result = TType.Double; 132 break; 133 case (byte)'i': 134 switch (name[1]) 135 { 136 case (byte)'8': 137 result = TType.Byte; 138 break; 139 case (byte)'1': 140 result = TType.I16; 141 break; 142 case (byte)'3': 143 result = TType.I32; 144 break; 145 case (byte)'6': 146 result = TType.I64; 147 break; 148 } 149 break; 150 case (byte)'l': 151 result = TType.List; 152 break; 153 case (byte)'m': 154 result = TType.Map; 155 break; 156 case (byte)'r': 157 result = TType.Struct; 158 break; 159 case (byte)'s': 160 if (name[1] == (byte)'t') 161 { 162 result = TType.String; 163 } 164 else if (name[1] == (byte)'e') 165 { 166 result = TType.Set; 167 } 168 break; 169 case (byte)'t': 170 result = TType.Bool; 171 break; 172 } 173 } 174 if (result == TType.Stop) 175 { 176 throw new TProtocolException(TProtocolException.NOT_IMPLEMENTED, 177 "Unrecognized type"); 178 } 179 return result; 180 } 181 182 /// <summary> 183 /// Base class for tracking JSON contexts that may require 184 /// inserting/Reading additional JSON syntax characters 185 /// This base context does nothing. 186 /// </summary> 187 protected class JSONBaseContext 188 { 189 protected TJSONProtocol proto; 190 JSONBaseContext(TJSONProtocol proto)191 public JSONBaseContext(TJSONProtocol proto) 192 { 193 this.proto = proto; 194 } 195 Write()196 public virtual void Write() { } 197 Read()198 public virtual void Read() { } 199 EscapeNumbers()200 public virtual bool EscapeNumbers() { return false; } 201 } 202 203 /// <summary> 204 /// Context for JSON lists. Will insert/Read commas before each item except 205 /// for the first one 206 /// </summary> 207 protected class JSONListContext : JSONBaseContext 208 { JSONListContext(TJSONProtocol protocol)209 public JSONListContext(TJSONProtocol protocol) 210 : base(protocol) 211 { 212 213 } 214 215 private bool first = true; 216 Write()217 public override void Write() 218 { 219 if (first) 220 { 221 first = false; 222 } 223 else 224 { 225 proto.trans.Write(COMMA); 226 } 227 } 228 Read()229 public override void Read() 230 { 231 if (first) 232 { 233 first = false; 234 } 235 else 236 { 237 proto.ReadJSONSyntaxChar(COMMA); 238 } 239 } 240 } 241 242 /// <summary> 243 /// Context for JSON records. Will insert/Read colons before the value portion 244 /// of each record pair, and commas before each key except the first. In 245 /// addition, will indicate that numbers in the key position need to be 246 /// escaped in quotes (since JSON keys must be strings). 247 /// </summary> 248 protected class JSONPairContext : JSONBaseContext 249 { JSONPairContext(TJSONProtocol proto)250 public JSONPairContext(TJSONProtocol proto) 251 : base(proto) 252 { 253 254 } 255 256 private bool first = true; 257 private bool colon = true; 258 Write()259 public override void Write() 260 { 261 if (first) 262 { 263 first = false; 264 colon = true; 265 } 266 else 267 { 268 proto.trans.Write(colon ? COLON : COMMA); 269 colon = !colon; 270 } 271 } 272 Read()273 public override void Read() 274 { 275 if (first) 276 { 277 first = false; 278 colon = true; 279 } 280 else 281 { 282 proto.ReadJSONSyntaxChar(colon ? COLON : COMMA); 283 colon = !colon; 284 } 285 } 286 EscapeNumbers()287 public override bool EscapeNumbers() 288 { 289 return colon; 290 } 291 } 292 293 /// <summary> 294 /// Holds up to one byte from the transport 295 /// </summary> 296 protected class LookaheadReader 297 { 298 protected TJSONProtocol proto; 299 LookaheadReader(TJSONProtocol proto)300 public LookaheadReader(TJSONProtocol proto) 301 { 302 this.proto = proto; 303 } 304 305 private bool hasData; 306 private byte[] data = new byte[1]; 307 308 /// <summary> 309 /// Return and consume the next byte to be Read, either taking it from the 310 /// data buffer if present or getting it from the transport otherwise. 311 /// </summary> Read()312 public byte Read() 313 { 314 if (hasData) 315 { 316 hasData = false; 317 } 318 else 319 { 320 proto.trans.ReadAll(data, 0, 1); 321 } 322 return data[0]; 323 } 324 325 /// <summary> 326 /// Return the next byte to be Read without consuming, filling the data 327 /// buffer if it has not been filled alReady. 328 /// </summary> Peek()329 public byte Peek() 330 { 331 if (!hasData) 332 { 333 proto.trans.ReadAll(data, 0, 1); 334 } 335 hasData = true; 336 return data[0]; 337 } 338 } 339 340 // Default encoding 341 protected Encoding utf8Encoding = UTF8Encoding.UTF8; 342 343 // Stack of nested contexts that we may be in 344 protected Stack<JSONBaseContext> contextStack = new Stack<JSONBaseContext>(); 345 346 // Current context that we are in 347 protected JSONBaseContext context; 348 349 // Reader that manages a 1-byte buffer 350 protected LookaheadReader reader; 351 352 /// <summary> 353 /// Push a new JSON context onto the stack. 354 /// </summary> PushContext(JSONBaseContext c)355 protected void PushContext(JSONBaseContext c) 356 { 357 contextStack.Push(context); 358 context = c; 359 } 360 361 /// <summary> 362 /// Pop the last JSON context off the stack 363 /// </summary> PopContext()364 protected void PopContext() 365 { 366 context = contextStack.Pop(); 367 } 368 369 /// <summary> 370 /// TJSONProtocol Constructor 371 /// </summary> TJSONProtocol(TTransport trans)372 public TJSONProtocol(TTransport trans) 373 : base(trans) 374 { 375 context = new JSONBaseContext(this); 376 reader = new LookaheadReader(this); 377 } 378 379 // Temporary buffer used by several methods 380 private byte[] tempBuffer = new byte[4]; 381 382 /// <summary> 383 /// Read a byte that must match b[0]; otherwise an exception is thrown. 384 /// Marked protected to avoid synthetic accessor in JSONListContext.Read 385 /// and JSONPairContext.Read 386 /// </summary> ReadJSONSyntaxChar(byte[] b)387 protected void ReadJSONSyntaxChar(byte[] b) 388 { 389 byte ch = reader.Read(); 390 if (ch != b[0]) 391 { 392 throw new TProtocolException(TProtocolException.INVALID_DATA, 393 "Unexpected character:" + (char)ch); 394 } 395 } 396 397 /// <summary> 398 /// Convert a byte containing a hex char ('0'-'9' or 'a'-'f') into its 399 /// corresponding hex value 400 /// </summary> HexVal(byte ch)401 private static byte HexVal(byte ch) 402 { 403 if ((ch >= '0') && (ch <= '9')) 404 { 405 return (byte)((char)ch - '0'); 406 } 407 else if ((ch >= 'a') && (ch <= 'f')) 408 { 409 ch += 10; 410 return (byte)((char)ch - 'a'); 411 } 412 else 413 { 414 throw new TProtocolException(TProtocolException.INVALID_DATA, 415 "Expected hex character"); 416 } 417 } 418 419 /// <summary> 420 /// Convert a byte containing a hex value to its corresponding hex character 421 /// </summary> HexChar(byte val)422 private static byte HexChar(byte val) 423 { 424 val &= 0x0F; 425 if (val < 10) 426 { 427 return (byte)((char)val + '0'); 428 } 429 else 430 { 431 val -= 10; 432 return (byte)((char)val + 'a'); 433 } 434 } 435 436 /// <summary> 437 /// Write the bytes in array buf as a JSON characters, escaping as needed 438 /// </summary> WriteJSONString(byte[] b)439 private void WriteJSONString(byte[] b) 440 { 441 context.Write(); 442 trans.Write(QUOTE); 443 int len = b.Length; 444 for (int i = 0; i < len; i++) 445 { 446 if ((b[i] & 0x00FF) >= 0x30) 447 { 448 if (b[i] == BACKSLASH[0]) 449 { 450 trans.Write(BACKSLASH); 451 trans.Write(BACKSLASH); 452 } 453 else 454 { 455 trans.Write(b, i, 1); 456 } 457 } 458 else 459 { 460 tempBuffer[0] = JSON_CHAR_TABLE[b[i]]; 461 if (tempBuffer[0] == 1) 462 { 463 trans.Write(b, i, 1); 464 } 465 else if (tempBuffer[0] > 1) 466 { 467 trans.Write(BACKSLASH); 468 trans.Write(tempBuffer, 0, 1); 469 } 470 else 471 { 472 trans.Write(ESCSEQ); 473 tempBuffer[0] = HexChar((byte)(b[i] >> 4)); 474 tempBuffer[1] = HexChar(b[i]); 475 trans.Write(tempBuffer, 0, 2); 476 } 477 } 478 } 479 trans.Write(QUOTE); 480 } 481 482 /// <summary> 483 /// Write out number as a JSON value. If the context dictates so, it will be 484 /// wrapped in quotes to output as a JSON string. 485 /// </summary> WriteJSONInteger(long num)486 private void WriteJSONInteger(long num) 487 { 488 context.Write(); 489 string str = num.ToString(); 490 491 bool escapeNum = context.EscapeNumbers(); 492 if (escapeNum) 493 trans.Write(QUOTE); 494 495 trans.Write(utf8Encoding.GetBytes(str)); 496 497 if (escapeNum) 498 trans.Write(QUOTE); 499 } 500 501 /// <summary> 502 /// Write out a double as a JSON value. If it is NaN or infinity or if the 503 /// context dictates escaping, Write out as JSON string. 504 /// </summary> WriteJSONDouble(double num)505 private void WriteJSONDouble(double num) 506 { 507 context.Write(); 508 string str = num.ToString("G17", CultureInfo.InvariantCulture); 509 bool special = false; 510 511 switch (str[0]) 512 { 513 case 'N': // NaN 514 case 'I': // Infinity 515 special = true; 516 break; 517 case '-': 518 if (str[1] == 'I') 519 { // -Infinity 520 special = true; 521 } 522 break; 523 } 524 525 bool escapeNum = special || context.EscapeNumbers(); 526 527 if (escapeNum) 528 trans.Write(QUOTE); 529 530 trans.Write(utf8Encoding.GetBytes(str)); 531 532 if (escapeNum) 533 trans.Write(QUOTE); 534 } 535 /// <summary> 536 /// Write out contents of byte array b as a JSON string with base-64 encoded 537 /// data 538 /// </summary> WriteJSONBase64(byte[] b)539 private void WriteJSONBase64(byte[] b) 540 { 541 context.Write(); 542 trans.Write(QUOTE); 543 544 int len = b.Length; 545 int off = 0; 546 547 while (len >= 3) 548 { 549 // Encode 3 bytes at a time 550 TBase64Utils.encode(b, off, 3, tempBuffer, 0); 551 trans.Write(tempBuffer, 0, 4); 552 off += 3; 553 len -= 3; 554 } 555 if (len > 0) 556 { 557 // Encode remainder 558 TBase64Utils.encode(b, off, len, tempBuffer, 0); 559 trans.Write(tempBuffer, 0, len + 1); 560 } 561 562 trans.Write(QUOTE); 563 } 564 WriteJSONObjectStart()565 private void WriteJSONObjectStart() 566 { 567 context.Write(); 568 trans.Write(LBRACE); 569 PushContext(new JSONPairContext(this)); 570 } 571 WriteJSONObjectEnd()572 private void WriteJSONObjectEnd() 573 { 574 PopContext(); 575 trans.Write(RBRACE); 576 } 577 WriteJSONArrayStart()578 private void WriteJSONArrayStart() 579 { 580 context.Write(); 581 trans.Write(LBRACKET); 582 PushContext(new JSONListContext(this)); 583 } 584 WriteJSONArrayEnd()585 private void WriteJSONArrayEnd() 586 { 587 PopContext(); 588 trans.Write(RBRACKET); 589 } 590 WriteMessageBegin(TMessage message)591 public override void WriteMessageBegin(TMessage message) 592 { 593 WriteJSONArrayStart(); 594 WriteJSONInteger(VERSION); 595 596 byte[] b = utf8Encoding.GetBytes(message.Name); 597 WriteJSONString(b); 598 599 WriteJSONInteger((long)message.Type); 600 WriteJSONInteger(message.SeqID); 601 } 602 WriteMessageEnd()603 public override void WriteMessageEnd() 604 { 605 WriteJSONArrayEnd(); 606 } 607 WriteStructBegin(TStruct str)608 public override void WriteStructBegin(TStruct str) 609 { 610 WriteJSONObjectStart(); 611 } 612 WriteStructEnd()613 public override void WriteStructEnd() 614 { 615 WriteJSONObjectEnd(); 616 } 617 WriteFieldBegin(TField field)618 public override void WriteFieldBegin(TField field) 619 { 620 WriteJSONInteger(field.ID); 621 WriteJSONObjectStart(); 622 WriteJSONString(GetTypeNameForTypeID(field.Type)); 623 } 624 WriteFieldEnd()625 public override void WriteFieldEnd() 626 { 627 WriteJSONObjectEnd(); 628 } 629 WriteFieldStop()630 public override void WriteFieldStop() { } 631 WriteMapBegin(TMap map)632 public override void WriteMapBegin(TMap map) 633 { 634 WriteJSONArrayStart(); 635 WriteJSONString(GetTypeNameForTypeID(map.KeyType)); 636 WriteJSONString(GetTypeNameForTypeID(map.ValueType)); 637 WriteJSONInteger(map.Count); 638 WriteJSONObjectStart(); 639 } 640 WriteMapEnd()641 public override void WriteMapEnd() 642 { 643 WriteJSONObjectEnd(); 644 WriteJSONArrayEnd(); 645 } 646 WriteListBegin(TList list)647 public override void WriteListBegin(TList list) 648 { 649 WriteJSONArrayStart(); 650 WriteJSONString(GetTypeNameForTypeID(list.ElementType)); 651 WriteJSONInteger(list.Count); 652 } 653 WriteListEnd()654 public override void WriteListEnd() 655 { 656 WriteJSONArrayEnd(); 657 } 658 WriteSetBegin(TSet set)659 public override void WriteSetBegin(TSet set) 660 { 661 WriteJSONArrayStart(); 662 WriteJSONString(GetTypeNameForTypeID(set.ElementType)); 663 WriteJSONInteger(set.Count); 664 } 665 WriteSetEnd()666 public override void WriteSetEnd() 667 { 668 WriteJSONArrayEnd(); 669 } 670 WriteBool(bool b)671 public override void WriteBool(bool b) 672 { 673 WriteJSONInteger(b ? (long)1 : (long)0); 674 } 675 WriteByte(sbyte b)676 public override void WriteByte(sbyte b) 677 { 678 WriteJSONInteger((long)b); 679 } 680 WriteI16(short i16)681 public override void WriteI16(short i16) 682 { 683 WriteJSONInteger((long)i16); 684 } 685 WriteI32(int i32)686 public override void WriteI32(int i32) 687 { 688 WriteJSONInteger((long)i32); 689 } 690 WriteI64(long i64)691 public override void WriteI64(long i64) 692 { 693 WriteJSONInteger(i64); 694 } 695 WriteDouble(double dub)696 public override void WriteDouble(double dub) 697 { 698 WriteJSONDouble(dub); 699 } 700 WriteString(string str)701 public override void WriteString(string str) 702 { 703 byte[] b = utf8Encoding.GetBytes(str); 704 WriteJSONString(b); 705 } 706 WriteBinary(byte[] bin)707 public override void WriteBinary(byte[] bin) 708 { 709 WriteJSONBase64(bin); 710 } 711 712 /** 713 * Reading methods. 714 */ 715 716 /// <summary> 717 /// Read in a JSON string, unescaping as appropriate.. Skip Reading from the 718 /// context if skipContext is true. 719 /// </summary> ReadJSONString(bool skipContext)720 private byte[] ReadJSONString(bool skipContext) 721 { 722 MemoryStream buffer = new MemoryStream(); 723 List<char> codeunits = new List<char>(); 724 725 726 if (!skipContext) 727 { 728 context.Read(); 729 } 730 ReadJSONSyntaxChar(QUOTE); 731 while (true) 732 { 733 byte ch = reader.Read(); 734 if (ch == QUOTE[0]) 735 { 736 break; 737 } 738 739 // escaped? 740 if (ch != ESCSEQ[0]) 741 { 742 buffer.Write(new byte[] { (byte)ch }, 0, 1); 743 continue; 744 } 745 746 // distinguish between \uXXXX and \? 747 ch = reader.Read(); 748 if (ch != ESCSEQ[1]) // control chars like \n 749 { 750 int off = Array.IndexOf(ESCAPE_CHARS, (char)ch); 751 if (off == -1) 752 { 753 throw new TProtocolException(TProtocolException.INVALID_DATA, 754 "Expected control char"); 755 } 756 ch = ESCAPE_CHAR_VALS[off]; 757 buffer.Write(new byte[] { (byte)ch }, 0, 1); 758 continue; 759 } 760 761 762 // it's \uXXXX 763 trans.ReadAll(tempBuffer, 0, 4); 764 var wch = (short)((HexVal((byte)tempBuffer[0]) << 12) + 765 (HexVal((byte)tempBuffer[1]) << 8) + 766 (HexVal((byte)tempBuffer[2]) << 4) + 767 HexVal(tempBuffer[3])); 768 if (Char.IsHighSurrogate((char)wch)) 769 { 770 if (codeunits.Count > 0) 771 { 772 throw new TProtocolException(TProtocolException.INVALID_DATA, 773 "Expected low surrogate char"); 774 } 775 codeunits.Add((char)wch); 776 } 777 else if (Char.IsLowSurrogate((char)wch)) 778 { 779 if (codeunits.Count == 0) 780 { 781 throw new TProtocolException(TProtocolException.INVALID_DATA, 782 "Expected high surrogate char"); 783 } 784 codeunits.Add((char)wch); 785 var tmp = utf8Encoding.GetBytes(codeunits.ToArray()); 786 buffer.Write(tmp, 0, tmp.Length); 787 codeunits.Clear(); 788 } 789 else 790 { 791 var tmp = utf8Encoding.GetBytes(new char[] { (char)wch }); 792 buffer.Write(tmp, 0, tmp.Length); 793 } 794 } 795 796 797 if (codeunits.Count > 0) 798 { 799 throw new TProtocolException(TProtocolException.INVALID_DATA, 800 "Expected low surrogate char"); 801 } 802 803 return buffer.ToArray(); 804 } 805 806 /// <summary> 807 /// Return true if the given byte could be a valid part of a JSON number. 808 /// </summary> IsJSONNumeric(byte b)809 private bool IsJSONNumeric(byte b) 810 { 811 switch (b) 812 { 813 case (byte)'+': 814 case (byte)'-': 815 case (byte)'.': 816 case (byte)'0': 817 case (byte)'1': 818 case (byte)'2': 819 case (byte)'3': 820 case (byte)'4': 821 case (byte)'5': 822 case (byte)'6': 823 case (byte)'7': 824 case (byte)'8': 825 case (byte)'9': 826 case (byte)'E': 827 case (byte)'e': 828 return true; 829 } 830 return false; 831 } 832 833 /// <summary> 834 /// Read in a sequence of characters that are all valid in JSON numbers. Does 835 /// not do a complete regex check to validate that this is actually a number. 836 /// </summary> ReadJSONNumericChars()837 private string ReadJSONNumericChars() 838 { 839 StringBuilder strbld = new StringBuilder(); 840 while (true) 841 { 842 byte ch = reader.Peek(); 843 if (!IsJSONNumeric(ch)) 844 { 845 break; 846 } 847 strbld.Append((char)reader.Read()); 848 } 849 return strbld.ToString(); 850 } 851 852 /// <summary> 853 /// Read in a JSON number. If the context dictates, Read in enclosing quotes. 854 /// </summary> ReadJSONInteger()855 private long ReadJSONInteger() 856 { 857 context.Read(); 858 if (context.EscapeNumbers()) 859 { 860 ReadJSONSyntaxChar(QUOTE); 861 } 862 863 string str = ReadJSONNumericChars(); 864 if (context.EscapeNumbers()) 865 { 866 ReadJSONSyntaxChar(QUOTE); 867 } 868 869 try 870 { 871 return Int64.Parse(str); 872 } 873 catch (FormatException fex) 874 { 875 throw new TProtocolException(TProtocolException.INVALID_DATA, 876 "Bad data encounted in numeric data", fex); 877 } 878 } 879 880 /// <summary> 881 /// Read in a JSON double value. Throw if the value is not wrapped in quotes 882 /// when expected or if wrapped in quotes when not expected. 883 /// </summary> ReadJSONDouble()884 private double ReadJSONDouble() 885 { 886 context.Read(); 887 if (reader.Peek() == QUOTE[0]) 888 { 889 byte[] arr = ReadJSONString(true); 890 double dub = Double.Parse(utf8Encoding.GetString(arr, 0, arr.Length), CultureInfo.InvariantCulture); 891 892 if (!context.EscapeNumbers() && !Double.IsNaN(dub) && !Double.IsInfinity(dub)) 893 { 894 // Throw exception -- we should not be in a string in this case 895 throw new TProtocolException(TProtocolException.INVALID_DATA, 896 "Numeric data unexpectedly quoted"); 897 } 898 return dub; 899 } 900 else 901 { 902 if (context.EscapeNumbers()) 903 { 904 // This will throw - we should have had a quote if escapeNum == true 905 ReadJSONSyntaxChar(QUOTE); 906 } 907 try 908 { 909 return Double.Parse(ReadJSONNumericChars(), CultureInfo.InvariantCulture); 910 } 911 catch (FormatException fex) 912 { 913 throw new TProtocolException(TProtocolException.INVALID_DATA, 914 "Bad data encounted in numeric data", fex); 915 } 916 } 917 } 918 919 /// <summary> 920 /// Read in a JSON string containing base-64 encoded data and decode it. 921 /// </summary> ReadJSONBase64()922 private byte[] ReadJSONBase64() 923 { 924 byte[] b = ReadJSONString(false); 925 int len = b.Length; 926 int off = 0; 927 int size = 0; 928 // reduce len to ignore fill bytes 929 while ((len > 0) && (b[len - 1] == '=')) 930 { 931 --len; 932 } 933 // read & decode full byte triplets = 4 source bytes 934 while (len > 4) 935 { 936 // Decode 4 bytes at a time 937 TBase64Utils.decode(b, off, 4, b, size); // NB: decoded in place 938 off += 4; 939 len -= 4; 940 size += 3; 941 } 942 // Don't decode if we hit the end or got a single leftover byte (invalid 943 // base64 but legal for skip of regular string type) 944 if (len > 1) 945 { 946 // Decode remainder 947 TBase64Utils.decode(b, off, len, b, size); // NB: decoded in place 948 size += len - 1; 949 } 950 // Sadly we must copy the byte[] (any way around this?) 951 byte[] result = new byte[size]; 952 Array.Copy(b, 0, result, 0, size); 953 return result; 954 } 955 ReadJSONObjectStart()956 private void ReadJSONObjectStart() 957 { 958 context.Read(); 959 ReadJSONSyntaxChar(LBRACE); 960 PushContext(new JSONPairContext(this)); 961 } 962 ReadJSONObjectEnd()963 private void ReadJSONObjectEnd() 964 { 965 ReadJSONSyntaxChar(RBRACE); 966 PopContext(); 967 } 968 ReadJSONArrayStart()969 private void ReadJSONArrayStart() 970 { 971 context.Read(); 972 ReadJSONSyntaxChar(LBRACKET); 973 PushContext(new JSONListContext(this)); 974 } 975 ReadJSONArrayEnd()976 private void ReadJSONArrayEnd() 977 { 978 ReadJSONSyntaxChar(RBRACKET); 979 PopContext(); 980 } 981 ReadMessageBegin()982 public override TMessage ReadMessageBegin() 983 { 984 TMessage message = new TMessage(); 985 ReadJSONArrayStart(); 986 if (ReadJSONInteger() != VERSION) 987 { 988 throw new TProtocolException(TProtocolException.BAD_VERSION, 989 "Message contained bad version."); 990 } 991 992 var buf = ReadJSONString(false); 993 message.Name = utf8Encoding.GetString(buf, 0, buf.Length); 994 message.Type = (TMessageType)ReadJSONInteger(); 995 message.SeqID = (int)ReadJSONInteger(); 996 return message; 997 } 998 ReadMessageEnd()999 public override void ReadMessageEnd() 1000 { 1001 ReadJSONArrayEnd(); 1002 } 1003 ReadStructBegin()1004 public override TStruct ReadStructBegin() 1005 { 1006 ReadJSONObjectStart(); 1007 return new TStruct(); 1008 } 1009 ReadStructEnd()1010 public override void ReadStructEnd() 1011 { 1012 ReadJSONObjectEnd(); 1013 } 1014 ReadFieldBegin()1015 public override TField ReadFieldBegin() 1016 { 1017 TField field = new TField(); 1018 byte ch = reader.Peek(); 1019 if (ch == RBRACE[0]) 1020 { 1021 field.Type = TType.Stop; 1022 } 1023 else 1024 { 1025 field.ID = (short)ReadJSONInteger(); 1026 ReadJSONObjectStart(); 1027 field.Type = GetTypeIDForTypeName(ReadJSONString(false)); 1028 } 1029 return field; 1030 } 1031 ReadFieldEnd()1032 public override void ReadFieldEnd() 1033 { 1034 ReadJSONObjectEnd(); 1035 } 1036 ReadMapBegin()1037 public override TMap ReadMapBegin() 1038 { 1039 TMap map = new TMap(); 1040 ReadJSONArrayStart(); 1041 map.KeyType = GetTypeIDForTypeName(ReadJSONString(false)); 1042 map.ValueType = GetTypeIDForTypeName(ReadJSONString(false)); 1043 map.Count = (int)ReadJSONInteger(); 1044 ReadJSONObjectStart(); 1045 return map; 1046 } 1047 ReadMapEnd()1048 public override void ReadMapEnd() 1049 { 1050 ReadJSONObjectEnd(); 1051 ReadJSONArrayEnd(); 1052 } 1053 ReadListBegin()1054 public override TList ReadListBegin() 1055 { 1056 TList list = new TList(); 1057 ReadJSONArrayStart(); 1058 list.ElementType = GetTypeIDForTypeName(ReadJSONString(false)); 1059 list.Count = (int)ReadJSONInteger(); 1060 return list; 1061 } 1062 ReadListEnd()1063 public override void ReadListEnd() 1064 { 1065 ReadJSONArrayEnd(); 1066 } 1067 ReadSetBegin()1068 public override TSet ReadSetBegin() 1069 { 1070 TSet set = new TSet(); 1071 ReadJSONArrayStart(); 1072 set.ElementType = GetTypeIDForTypeName(ReadJSONString(false)); 1073 set.Count = (int)ReadJSONInteger(); 1074 return set; 1075 } 1076 ReadSetEnd()1077 public override void ReadSetEnd() 1078 { 1079 ReadJSONArrayEnd(); 1080 } 1081 ReadBool()1082 public override bool ReadBool() 1083 { 1084 return (ReadJSONInteger() == 0 ? false : true); 1085 } 1086 ReadByte()1087 public override sbyte ReadByte() 1088 { 1089 return (sbyte)ReadJSONInteger(); 1090 } 1091 ReadI16()1092 public override short ReadI16() 1093 { 1094 return (short)ReadJSONInteger(); 1095 } 1096 ReadI32()1097 public override int ReadI32() 1098 { 1099 return (int)ReadJSONInteger(); 1100 } 1101 ReadI64()1102 public override long ReadI64() 1103 { 1104 return (long)ReadJSONInteger(); 1105 } 1106 ReadDouble()1107 public override double ReadDouble() 1108 { 1109 return ReadJSONDouble(); 1110 } 1111 ReadString()1112 public override string ReadString() 1113 { 1114 var buf = ReadJSONString(false); 1115 return utf8Encoding.GetString(buf, 0, buf.Length); 1116 } 1117 ReadBinary()1118 public override byte[] ReadBinary() 1119 { 1120 return ReadJSONBase64(); 1121 } 1122 1123 } 1124 } 1125