FormUrlEncodedReader.cs 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. using System;
  2. using System.IO;
  3. using System.Text;
  4. namespace Google.ProtocolBuffers.Serialization.Http
  5. {
  6. /// <summary>
  7. /// Allows reading messages from a name/value dictionary
  8. /// </summary>
  9. public class FormUrlEncodedReader : AbstractTextReader
  10. {
  11. private readonly TextReader _input;
  12. private string _fieldName, _fieldValue;
  13. private bool _ready;
  14. /// <summary>
  15. /// Creates a dictionary reader from an enumeration of KeyValuePair data, like an IDictionary
  16. /// </summary>
  17. FormUrlEncodedReader(TextReader input)
  18. {
  19. _input = input;
  20. int ch = input.Peek();
  21. if (ch == '?')
  22. {
  23. input.Read();
  24. }
  25. _ready = ReadNext();
  26. }
  27. #region CreateInstance overloads
  28. /// <summary>
  29. /// Constructs a FormUrlEncodedReader to parse form data, or url query text into a message.
  30. /// </summary>
  31. public static FormUrlEncodedReader CreateInstance(Stream stream)
  32. {
  33. return new FormUrlEncodedReader(new StreamReader(stream, Encoding.UTF8, false));
  34. }
  35. /// <summary>
  36. /// Constructs a FormUrlEncodedReader to parse form data, or url query text into a message.
  37. /// </summary>
  38. public static FormUrlEncodedReader CreateInstance(byte[] bytes)
  39. {
  40. return new FormUrlEncodedReader(new StreamReader(new MemoryStream(bytes, false), Encoding.UTF8, false));
  41. }
  42. /// <summary>
  43. /// Constructs a FormUrlEncodedReader to parse form data, or url query text into a message.
  44. /// </summary>
  45. public static FormUrlEncodedReader CreateInstance(string text)
  46. {
  47. return new FormUrlEncodedReader(new StringReader(text));
  48. }
  49. /// <summary>
  50. /// Constructs a FormUrlEncodedReader to parse form data, or url query text into a message.
  51. /// </summary>
  52. public static FormUrlEncodedReader CreateInstance(TextReader input)
  53. {
  54. return new FormUrlEncodedReader(input);
  55. }
  56. #endregion
  57. private bool ReadNext()
  58. {
  59. StringBuilder field = new StringBuilder(32);
  60. StringBuilder value = new StringBuilder(64);
  61. int ch;
  62. while (-1 != (ch = _input.Read()) && ch != '=' && ch != '&')
  63. {
  64. field.Append((char)ch);
  65. }
  66. if (ch != -1 && ch != '&')
  67. {
  68. while (-1 != (ch = _input.Read()) && ch != '&')
  69. {
  70. value.Append((char)ch);
  71. }
  72. }
  73. _fieldName = field.ToString();
  74. _fieldValue = Uri.UnescapeDataString(value.Replace('+', ' ').ToString());
  75. return !String.IsNullOrEmpty(_fieldName);
  76. }
  77. /// <summary>
  78. /// No-op
  79. /// </summary>
  80. public override void ReadMessageStart()
  81. { }
  82. /// <summary>
  83. /// No-op
  84. /// </summary>
  85. public override void ReadMessageEnd()
  86. { }
  87. /// <summary>
  88. /// Merges the contents of stream into the provided message builder
  89. /// </summary>
  90. public override TBuilder Merge<TBuilder>(TBuilder builder, ExtensionRegistry registry)
  91. {
  92. builder.WeakMergeFrom(this, registry);
  93. return builder;
  94. }
  95. /// <summary>
  96. /// Causes the reader to skip past this field
  97. /// </summary>
  98. protected override void Skip()
  99. {
  100. _ready = ReadNext();
  101. }
  102. /// <summary>
  103. /// Peeks at the next field in the input stream and returns what information is available.
  104. /// </summary>
  105. /// <remarks>
  106. /// This may be called multiple times without actually reading the field. Only after the field
  107. /// is either read, or skipped, should PeekNext return a different value.
  108. /// </remarks>
  109. protected override bool PeekNext(out string field)
  110. {
  111. field = _ready ? _fieldName : null;
  112. return field != null;
  113. }
  114. /// <summary>
  115. /// Returns true if it was able to read a String from the input
  116. /// </summary>
  117. protected override bool ReadAsText(ref string value, Type typeInfo)
  118. {
  119. if (_ready)
  120. {
  121. value = _fieldValue;
  122. _ready = ReadNext();
  123. return true;
  124. }
  125. return false;
  126. }
  127. /// <summary>
  128. /// It's unlikely this will work for anything but text data as bytes UTF8 are transformed to text and back to bytes
  129. /// </summary>
  130. protected override ByteString DecodeBytes(string bytes)
  131. { return ByteString.CopyFromUtf8(bytes); }
  132. /// <summary>
  133. /// Not Supported
  134. /// </summary>
  135. public override bool ReadGroup(IBuilderLite value, ExtensionRegistry registry)
  136. { throw new NotSupportedException(); }
  137. /// <summary>
  138. /// Not Supported
  139. /// </summary>
  140. protected override bool ReadMessage(IBuilderLite builder, ExtensionRegistry registry)
  141. { throw new NotSupportedException(); }
  142. }
  143. }