You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

255 lines
8.3 KiB

4 years ago
  1. #if MYSQL_6_10
  2. // Copyright � 2004, 2016, Oracle and/or its affiliates. All rights reserved.
  3. //
  4. // MySQL Connector/NET is licensed under the terms of the GPLv2
  5. // <http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>, like most
  6. // MySQL Connectors. There are special exceptions to the terms and
  7. // conditions of the GPLv2 as it is applied to this software, see the
  8. // FLOSS License Exception
  9. // <http://www.mysql.com/about/legal/licensing/foss-exception.html>.
  10. //
  11. // This program is free software; you can redistribute it and/or modify
  12. // it under the terms of the GNU General Public License as published
  13. // by the Free Software Foundation; version 2 of the License.
  14. //
  15. // This program is distributed in the hope that it will be useful, but
  16. // WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
  17. // or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
  18. // for more details.
  19. //
  20. // You should have received a copy of the GNU General Public License along
  21. // with this program; if not, write to the Free Software Foundation, Inc.,
  22. // 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
  23. using System;
  24. using System.IO;
  25. using System.Diagnostics;
  26. using System.Text;
  27. using Externals.MySql.Data.Common;
  28. namespace Externals.MySql.Data.MySqlClient
  29. {
  30. /// <summary>
  31. /// Summary description for MySqlStream.
  32. /// </summary>
  33. internal class MySqlStream
  34. {
  35. private byte sequenceByte;
  36. private int maxBlockSize;
  37. private ulong maxPacketSize;
  38. private byte[] packetHeader = new byte[4];
  39. MySqlPacket packet;
  40. TimedStream timedStream;
  41. Stream inStream;
  42. Stream outStream;
  43. internal Stream BaseStream
  44. {
  45. get
  46. {
  47. return timedStream;
  48. }
  49. }
  50. public MySqlStream(Encoding encoding)
  51. {
  52. // we have no idea what the real value is so we start off with the max value
  53. // The real value will be set in NativeDriver.Configure()
  54. maxPacketSize = ulong.MaxValue;
  55. // we default maxBlockSize to MaxValue since we will get the 'real' value in
  56. // the authentication handshake and we know that value will not exceed
  57. // true maxBlockSize prior to that.
  58. maxBlockSize = Int32.MaxValue;
  59. packet = new MySqlPacket(encoding);
  60. }
  61. public MySqlStream(Stream baseStream, Encoding encoding, bool compress)
  62. : this(encoding)
  63. {
  64. timedStream = new TimedStream(baseStream);
  65. Stream stream;
  66. if (compress)
  67. stream = new CompressedStream(timedStream);
  68. else
  69. stream = timedStream;
  70. inStream = stream;
  71. outStream = stream;
  72. }
  73. public void Close()
  74. {
  75. outStream.Dispose();
  76. inStream.Dispose();
  77. timedStream.Close();
  78. }
  79. #region Properties
  80. public Encoding Encoding
  81. {
  82. get { return packet.Encoding; }
  83. set { packet.Encoding = value; }
  84. }
  85. public void ResetTimeout(int timeout)
  86. {
  87. timedStream.ResetTimeout(timeout);
  88. }
  89. public byte SequenceByte
  90. {
  91. get { return sequenceByte; }
  92. set { sequenceByte = value; }
  93. }
  94. public int MaxBlockSize
  95. {
  96. get { return maxBlockSize; }
  97. set { maxBlockSize = value; }
  98. }
  99. public ulong MaxPacketSize
  100. {
  101. get { return maxPacketSize; }
  102. set { maxPacketSize = value; }
  103. }
  104. #endregion
  105. #region Packet methods
  106. /// <summary>
  107. /// ReadPacket is called by NativeDriver to start reading the next
  108. /// packet on the stream.
  109. /// </summary>
  110. public MySqlPacket ReadPacket()
  111. {
  112. //Debug.Assert(packet.Position == packet.Length);
  113. // make sure we have read all the data from the previous packet
  114. //Debug.Assert(HasMoreData == false, "HasMoreData is true in OpenPacket");
  115. LoadPacket();
  116. // now we check if this packet is a server error
  117. if (packet.Buffer[0] == 0xff)
  118. {
  119. packet.ReadByte(); // read off the 0xff
  120. int code = packet.ReadInteger(2);
  121. string msg = String.Empty;
  122. if (packet.Version.isAtLeast(5, 5, 0))
  123. msg = packet.ReadString(Encoding.UTF8);
  124. else
  125. msg = packet.ReadString();
  126. if (msg.StartsWith("#", StringComparison.Ordinal))
  127. {
  128. msg.Substring(1, 5); /* state code */
  129. msg = msg.Substring(6);
  130. }
  131. throw new MySqlException(msg, code);
  132. }
  133. return packet;
  134. }
  135. /// <summary>
  136. /// Reads the specified number of bytes from the stream and stores them at given
  137. /// offset in the buffer.
  138. /// Throws EndOfStreamException if not all bytes can be read.
  139. /// </summary>
  140. /// <param name="stream">Stream to read from</param>
  141. /// <param name="buffer"> Array to store bytes read from the stream </param>
  142. /// <param name="offset">The offset in buffer at which to begin storing the data read from the current stream. </param>
  143. /// <param name="count">Number of bytes to read</param>
  144. internal static void ReadFully(Stream stream, byte[] buffer, int offset, int count)
  145. {
  146. int numRead = 0;
  147. int numToRead = count;
  148. while (numToRead > 0)
  149. {
  150. int read = stream.Read(buffer, offset + numRead, numToRead);
  151. if (read == 0)
  152. {
  153. throw new EndOfStreamException();
  154. }
  155. numRead += read;
  156. numToRead -= read;
  157. }
  158. }
  159. /// <summary>
  160. /// LoadPacket loads up and decodes the header of the incoming packet.
  161. /// </summary>
  162. public void LoadPacket()
  163. {
  164. try
  165. {
  166. packet.Length = 0;
  167. int offset = 0;
  168. while (true)
  169. {
  170. ReadFully(inStream, packetHeader, 0, 4);
  171. sequenceByte = (byte)(packetHeader[3] + 1);
  172. int length = (int)(packetHeader[0] + (packetHeader[1] << 8) +
  173. (packetHeader[2] << 16));
  174. // make roo for the next block
  175. packet.Length += length;
  176. ReadFully(inStream, packet.Buffer, offset, length);
  177. offset += length;
  178. // if this block was < maxBlock then it's last one in a multipacket series
  179. if (length < maxBlockSize) break;
  180. }
  181. packet.Position = 0;
  182. }
  183. catch (IOException ioex)
  184. {
  185. throw new MySqlException(Resources.ReadFromStreamFailed, true, ioex);
  186. }
  187. }
  188. public void SendPacket(MySqlPacket packet)
  189. {
  190. byte[] buffer = packet.Buffer;
  191. int length = packet.Position - 4;
  192. if ((ulong)length > maxPacketSize)
  193. throw new MySqlException(Resources.QueryTooLarge, (int)MySqlErrorCode.PacketTooLarge);
  194. int offset = 0;
  195. do
  196. {
  197. int lenToSend = length > maxBlockSize ? maxBlockSize : length;
  198. buffer[offset] = (byte)(lenToSend & 0xff);
  199. buffer[offset + 1] = (byte)((lenToSend >> 8) & 0xff);
  200. buffer[offset + 2] = (byte)((lenToSend >> 16) & 0xff);
  201. buffer[offset + 3] = sequenceByte++;
  202. outStream.Write(buffer, offset, lenToSend + 4);
  203. outStream.Flush();
  204. length -= lenToSend;
  205. offset += lenToSend;
  206. } while (length > 0);
  207. }
  208. public void SendEntirePacketDirectly(byte[] buffer, int count)
  209. {
  210. buffer[0] = (byte)(count & 0xff);
  211. buffer[1] = (byte)((count >> 8) & 0xff);
  212. buffer[2] = (byte)((count >> 16) & 0xff);
  213. buffer[3] = sequenceByte++;
  214. outStream.Write(buffer, 0, count + 4);
  215. outStream.Flush();
  216. }
  217. #endregion
  218. }
  219. }
  220. #endif