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.

462 lines
17 KiB

4 years ago
  1. #if MYSQL_6_10
  2. // Copyright © 2013, 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.Collections.Generic;
  25. using System.IO;
  26. using System.Text;
  27. namespace Externals.MySql.Data.MySqlClient.Memcached
  28. {
  29. /// <summary>
  30. /// Implementation of memcached binary client protocol.
  31. /// </summary>
  32. /// <remarks>According to http://code.google.com/p/memcached/wiki/BinaryProtocolRevamped </remarks>
  33. internal class BinaryClient : Client
  34. {
  35. private readonly Encoding _encoding;
  36. private enum OpCodes : byte
  37. {
  38. Get = 0x00,
  39. Set = 0x01,
  40. Add = 0x02,
  41. Replace = 0x03,
  42. Delete = 0x04,
  43. Increment = 0x05,
  44. Decrement = 0x06,
  45. Quit = 0x07,
  46. Flush = 0x08,
  47. GetK = 0x0c,
  48. GetKQ = 0x0d,
  49. Append = 0x0e,
  50. Prepend = 0x0f,
  51. SASL_list_mechs = 0x20,
  52. SASL_Auth = 0x21,
  53. SASL_Step = 0x22
  54. }
  55. private enum MagicByte : byte
  56. {
  57. Request = 0x80,
  58. Response = 0x81
  59. }
  60. private enum ResponseStatus : ushort
  61. {
  62. NoError = 0x0000,
  63. KeyNotFound = 0x0001,
  64. KeyExists = 0x0002,
  65. ValueTooLarge = 0x0003,
  66. InvalidArguments = 0x0004,
  67. ItemNotStored = 0x0005,
  68. IncrDecrOnNonNumericValue = 0x0006,
  69. VbucketBelongsToAnotherServer = 0x0007,
  70. AuthenticationError = 0x0008,
  71. AuthenticationContinue = 0x0009,
  72. UnknownCommand = 0x0081,
  73. OutOfMemory = 0x0082,
  74. NotSupported = 0x0083,
  75. InternalError = 0x0084,
  76. Busy = 0x0085,
  77. TemporaryFailure = 0x0086
  78. }
  79. public BinaryClient(string server, uint port) : base(server, port)
  80. {
  81. _encoding = Encoding.UTF8;
  82. }
  83. #region Memcached protocol interface
  84. public override void Add(string key, object data, TimeSpan expiration)
  85. {
  86. SendCommand((byte)MagicByte.Request, (byte)OpCodes.Add, key, data, expiration, true);
  87. }
  88. public override void Append(string key, object data)
  89. {
  90. SendCommand((byte)MagicByte.Request, (byte)OpCodes.Append, key, data, TimeSpan.Zero, false);
  91. }
  92. public override void Cas(string key, object data, TimeSpan expiration, ulong casUnique)
  93. {
  94. throw new NotImplementedException("Not available in binary protocol");
  95. //SendCommand((byte)MagicByte.Request, (byte)OpCodes.Cas, key, data, expiration, true, casUnique);
  96. }
  97. public override void Decrement(string key, int amount)
  98. {
  99. SendCommand((byte)MagicByte.Request, (byte)OpCodes.Decrement, key, amount);
  100. }
  101. public override void Delete(string key)
  102. {
  103. SendCommand((byte)MagicByte.Request, (byte)OpCodes.Delete, key);
  104. }
  105. public override void FlushAll(TimeSpan delay)
  106. {
  107. SendCommand((byte)MagicByte.Request, (byte)OpCodes.Flush, delay);
  108. }
  109. public override KeyValuePair<string, object> Get(string key)
  110. {
  111. string val;
  112. SendCommand((byte)MagicByte.Request, (byte)OpCodes.Get, key, out val);
  113. return new KeyValuePair<string, object>(key, val);
  114. }
  115. public override void Increment(string key, int amount)
  116. {
  117. SendCommand((byte)MagicByte.Request, (byte)OpCodes.Increment, key, amount);
  118. }
  119. public override void Prepend(string key, object data)
  120. {
  121. SendCommand((byte)MagicByte.Request, (byte)OpCodes.Prepend, key, data, TimeSpan.Zero, false);
  122. }
  123. public override void Replace(string key, object data, TimeSpan expiration)
  124. {
  125. SendCommand((byte)MagicByte.Request, (byte)OpCodes.Replace, key, data, expiration, true);
  126. }
  127. public override void Set(string key, object data, TimeSpan expiration)
  128. {
  129. SendCommand((byte)MagicByte.Request, (byte)OpCodes.Set, key, data, expiration, true);
  130. }
  131. #endregion
  132. /// <summary>
  133. /// Sends an store command (add, replace, set).
  134. /// </summary>
  135. /// <param name="magic"></param>
  136. /// <param name="opcode"></param>
  137. /// <param name="key"></param>
  138. /// <param name="data"></param>
  139. /// <param name="expiration"></param>
  140. /// <param name="hasExtra"></param>
  141. private void SendCommand(
  142. byte magic, byte opcode, string key, object data, TimeSpan expiration, bool hasExtra)
  143. {
  144. // Send data
  145. byte[] dataToSend = EncodeStoreCommand(magic, opcode, key, data, expiration, hasExtra);
  146. stream.Write(dataToSend, 0, dataToSend.Length);
  147. byte[] res = GetResponse();
  148. }
  149. /// <summary>
  150. /// Sends a get command.
  151. /// </summary>
  152. /// <param name="magic"></param>
  153. /// <param name="opcode"></param>
  154. /// <param name="key"></param>
  155. /// <param name="value"></param>
  156. private void SendCommand(byte magic, byte opcode, string key, out string value)
  157. {
  158. // Send data
  159. byte[] dataToSend = EncodeGetCommand(magic, opcode, key);
  160. stream.Write(dataToSend, 0, dataToSend.Length);
  161. byte[] res = GetResponse();
  162. byte[] bValue = new byte[res[4] - 4];
  163. Array.Copy(res, 28, bValue, 0, res[4] - 4);
  164. value = _encoding.GetString(bValue, 0, bValue.Length);
  165. }
  166. /// <summary>
  167. /// Sends a delete command.
  168. /// </summary>
  169. /// <param name="magic"></param>
  170. /// <param name="opcode"></param>
  171. /// <param name="key"></param>
  172. private void SendCommand(byte magic, byte opcode, string key)
  173. {
  174. // Send data
  175. byte[] dataToSend = EncodeGetCommand(magic, opcode, key);
  176. stream.Write(dataToSend, 0, dataToSend.Length);
  177. byte[] res = GetResponse();
  178. }
  179. /// <summary>
  180. /// Sends a command without args (like flush).
  181. /// </summary>
  182. /// <param name="magic"></param>
  183. /// <param name="opcode"></param>
  184. /// <param name="expiration"></param>
  185. private void SendCommand(byte magic, byte opcode, TimeSpan expiration)
  186. {
  187. // Send data
  188. byte[] dataToSend = EncodeFlushCommand(magic, opcode, expiration);
  189. stream.Write(dataToSend, 0, dataToSend.Length);
  190. byte[] res = GetResponse();
  191. }
  192. /// <summary>
  193. /// Sends a command with amount (INCR/DECR)
  194. /// </summary>
  195. /// <param name="magic"></param>
  196. /// <param name="opcode"></param>
  197. /// <param name="key"></param>
  198. /// <param name="amount"></param>
  199. private void SendCommand(byte magic, byte opcode, string key, int amount)
  200. {
  201. // Send data
  202. byte[] dataToSend = EncodeIncrCommand(magic, opcode, key, amount);
  203. stream.Write(dataToSend, 0, dataToSend.Length);
  204. byte[] res = GetResponse();
  205. }
  206. private byte[] GetResponse()
  207. {
  208. byte[] response = new byte[24];
  209. stream.Read(response, 0, response.Length);
  210. ValidateResponse(response);
  211. return response;
  212. }
  213. private void ValidateResponse(byte[] res)
  214. {
  215. // Memcached returns words in big endian.
  216. ushort status = (ushort)((res[6] << 8) | res[7]);
  217. if (status != 0)
  218. {
  219. throw new MemcachedException(((ResponseStatus)status).ToString());
  220. }
  221. }
  222. /// <summary>
  223. /// Encodes in the binary protocol the a command of the kind set, add or replace.
  224. /// </summary>
  225. /// <param name="magic"></param>
  226. /// <param name="opcode"></param>
  227. /// <param name="key"></param>
  228. /// <param name="data"></param>
  229. /// <param name="expiration"></param>
  230. /// <param name="hasExtra">If true applies to set, add or replace commands; if false applies to append and prepend commands.</param>
  231. /// <returns></returns>
  232. private byte[] EncodeStoreCommand(
  233. byte magic, byte opcode, string key, object data, TimeSpan expiration,
  234. bool hasExtra)
  235. {
  236. /*
  237. * Field (offset) (value)
  238. Magic (0) : 0x80
  239. Opcode (1) : 0x02
  240. Key length (2,3) : 0x0005
  241. Extra length (4) : 0x08
  242. Data type (5) : 0x00
  243. VBucket (6,7) : 0x0000
  244. Total body (8-11) : 0x00000012
  245. Opaque (12-15): 0x00000000
  246. CAS (16-23): 0x0000000000000000
  247. Extras :
  248. Flags (24-27): 0xdeadbeef
  249. Expiry (28-31): 0x00000e10
  250. Key (32-36): The textual string "Hello"
  251. Value (37-41): The textual string "World"
  252. * */
  253. byte[] bKey = _encoding.GetBytes(key);
  254. byte[] bData = _encoding.GetBytes(data.ToString());
  255. MemoryStream ms = new MemoryStream();
  256. // write magic
  257. ms.WriteByte(magic);
  258. // write opcode
  259. ms.WriteByte(opcode);
  260. // write keylength
  261. WriteToMemoryStream(BitConverter.GetBytes((ushort)bKey.Length), ms);
  262. // write extra length
  263. ms.WriteByte(8);
  264. // write data type
  265. ms.WriteByte(0);
  266. // write status
  267. ms.WriteByte(0); ms.WriteByte(0);
  268. // write total body length
  269. WriteToMemoryStream(BitConverter.GetBytes((uint)
  270. (bKey.Length + bData.Length + (hasExtra ? 8 : 0))), ms);
  271. // write opaque
  272. WriteToMemoryStream(BitConverter.GetBytes((uint)0), ms);
  273. // write CAS
  274. // NOTE: For some reason in the Innodb implementation of Memcached the CAS
  275. // is 4 bytes long (instead of 8 bytes).
  276. WriteToMemoryStream(BitConverter.GetBytes((ushort)0), ms);
  277. // write extras, flags
  278. if (hasExtra)
  279. {
  280. ms.Write(new byte[4], 0, 4);
  281. WriteToMemoryStream(BitConverter.GetBytes((uint)(expiration.TotalSeconds)), ms);
  282. }
  283. // write key
  284. ms.Write(bKey, 0, bKey.Length);
  285. // write value
  286. ms.Write(bData, 0, bData.Length);
  287. return ms.ToArray();
  288. }
  289. private byte[] EncodeGetCommand(byte magic, byte opcode, string key)
  290. {
  291. /*
  292. * Field (offset) (value)
  293. Magic (0) : 0x80
  294. Opcode (1) : 0x00
  295. Key length (2,3) : 0x0005
  296. Extra length (4) : 0x00
  297. Data type (5) : 0x00
  298. VBucket (6,7) : 0x0000
  299. Total body (8-11) : 0x00000005
  300. Opaque (12-15): 0x00000000
  301. CAS (16-23): 0x0000000000000000
  302. Extras : None
  303. Key (24-29): The textual string: "Hello"
  304. Value : None
  305. * */
  306. byte[] bKey = _encoding.GetBytes(key);
  307. MemoryStream ms = new MemoryStream();
  308. // write magic
  309. ms.WriteByte(magic);
  310. // write opcode
  311. ms.WriteByte(opcode);
  312. // write keylength
  313. WriteToMemoryStream(BitConverter.GetBytes((ushort)bKey.Length), ms);
  314. // write extra length
  315. ms.WriteByte(8);
  316. // write data type
  317. ms.WriteByte(0);
  318. // write status
  319. ms.WriteByte(0); ms.WriteByte(0);
  320. // write total body length
  321. WriteToMemoryStream(BitConverter.GetBytes((ushort)bKey.Length), ms);
  322. // write opaque
  323. WriteToMemoryStream(BitConverter.GetBytes((uint)0), ms);
  324. // write CAS
  325. WriteToMemoryStream(BitConverter.GetBytes((uint)0), ms);
  326. // write key
  327. ms.Write(bKey, 0, bKey.Length);
  328. return ms.ToArray();
  329. }
  330. private byte[] EncodeFlushCommand(byte magic, byte opcode, TimeSpan expiration)
  331. {
  332. /*
  333. * Field (offset) (value)
  334. Magic (0) : 0x80
  335. Opcode (1) : 0x08
  336. Key length (2,3) : 0x0000
  337. Extra length (4) : 0x04
  338. Data type (5) : 0x00
  339. VBucket (6,7) : 0x0000
  340. Total body (8-11) : 0x00000004
  341. Opaque (12-15): 0x00000000
  342. CAS (16-23): 0x0000000000000000
  343. Extras :
  344. Expiry (24-27): 0x000e10
  345. Key : None
  346. Value : None
  347. * */
  348. MemoryStream ms = new MemoryStream();
  349. // write magic
  350. ms.WriteByte(magic);
  351. // write opcode
  352. ms.WriteByte(opcode);
  353. // write keylength
  354. ms.WriteByte(0); ms.WriteByte(0);
  355. // write extra length
  356. ms.WriteByte(4);
  357. // write data type
  358. ms.WriteByte(0);
  359. // write status
  360. ms.WriteByte(0); ms.WriteByte(0);
  361. // write total body length
  362. WriteToMemoryStream(BitConverter.GetBytes((ushort)4), ms);
  363. // write opaque
  364. WriteToMemoryStream(BitConverter.GetBytes((uint)0), ms);
  365. // write CAS
  366. WriteToMemoryStream(BitConverter.GetBytes((uint)0), ms);
  367. // write extra (flags)
  368. WriteToMemoryStream(BitConverter.GetBytes((uint)(expiration.TotalSeconds)), ms);
  369. return ms.ToArray();
  370. }
  371. private byte[] EncodeIncrCommand(byte magic, byte opcode, string key, int amount)
  372. {
  373. /*
  374. * Field (offset) (value)
  375. Magic (0) : 0x80
  376. Opcode (1) : 0x05
  377. Key length (2,3) : 0x0007
  378. Extra length (4) : 0x14
  379. Data type (5) : 0x00
  380. VBucket (6,7) : 0x0000
  381. Total body (8-11) : 0x0000001b
  382. Opaque (12-15): 0x00000000
  383. CAS (16-23): 0x0000000000000000
  384. Extras :
  385. delta (24-31): 0x0000000000000001
  386. initial (32-39): 0x0000000000000000
  387. exipration (40-43): 0x00000e10
  388. Key : Textual string "counter"
  389. Value : None
  390. * */
  391. byte[] bKey = _encoding.GetBytes(key);
  392. MemoryStream ms = new MemoryStream();
  393. // write magic
  394. ms.WriteByte(magic);
  395. // write opcode
  396. ms.WriteByte(opcode);
  397. // write keylength
  398. WriteToMemoryStream(BitConverter.GetBytes((ushort)bKey.Length), ms);
  399. // write extra length
  400. ms.WriteByte(20);
  401. // write data type
  402. ms.WriteByte(0);
  403. // write status
  404. ms.WriteByte(0); ms.WriteByte(0);
  405. // write total body length
  406. WriteToMemoryStream(BitConverter.GetBytes((ushort)(bKey.Length + 20)), ms);
  407. // write opaque
  408. WriteToMemoryStream(BitConverter.GetBytes((uint)0), ms);
  409. // write CAS
  410. WriteToMemoryStream(BitConverter.GetBytes((uint)0), ms);
  411. // write extra (flags)
  412. long delta = amount;
  413. if ((OpCodes)opcode == OpCodes.Decrement)
  414. delta *= -1;
  415. WriteToMemoryStream(BitConverter.GetBytes((long)0), ms);
  416. WriteToMemoryStream(BitConverter.GetBytes((uint)(TimeSpan.Zero.TotalSeconds)), ms);
  417. // write key
  418. ms.Write(bKey, 0, bKey.Length);
  419. return ms.ToArray();
  420. }
  421. private void WriteToMemoryStream(byte[] data, MemoryStream ms)
  422. {
  423. // .NET runs in x86 which uses little endian, and Memcached runs in big endian.
  424. Array.Reverse(data);
  425. ms.Write(data, 0, data.Length);
  426. }
  427. }
  428. }
  429. #endif