PageRenderTime 50ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 0ms

/Otp/AbstractConnection.cs

https://github.com/saleyn/otp.net
C# | 1736 lines | 1216 code | 226 blank | 294 comment | 150 complexity | ecb351129630aa6ab4b985d068eb9e93 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

  1. /*``The contents of this file are subject to the Erlang Public License,
  2. * Version 1.1, (the "License"); you may not use this file except in
  3. * compliance with the License. You should have received a copy of the
  4. * Erlang Public License along with this software. If not, it can be
  5. * retrieved via the world wide web at http://www.erlang.org/.
  6. *
  7. * Software distributed under the License is distributed on an "AS IS"
  8. * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
  9. * the License for the specific language governing rights and limitations
  10. * under the License.
  11. *
  12. * The Initial Developer of the Original Code is Ericsson Utvecklings AB.
  13. * Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
  14. * AB. All Rights Reserved.''
  15. *
  16. * Converted from Java to C# by Vlad Dumitrescu (vlad_Dumitrescu@hotmail.com)
  17. */
  18. using System;
  19. using System.Diagnostics;
  20. using System.Threading;
  21. namespace Otp
  22. {
  23. /*
  24. * Maintains a connection between a C# process and a remote Erlang,
  25. * C# or C node. The object maintains connection state and allows
  26. * data to be sent to and received from the peer.
  27. *
  28. * <p> This abstract class provides the neccesary methods to maintain
  29. * the actual connection and encode the messages and headers in the
  30. * proper format according to the Erlang distribution protocol.
  31. * Subclasses can use these methods to provide a more or less
  32. * transparent communication channel as desired. </p>
  33. *
  34. * <p> Note that no receive methods are provided. Subclasses must
  35. * provide methods for message delivery, and may implement their own
  36. * receive methods. <p>
  37. *
  38. * <p> If an exception occurs in any of the methods in this class, the
  39. * connection will be closed and must be reopened in order to resume
  40. * communication with the peer. This will be indicated to the subclass
  41. * by passing the exception to its delivery() method. </p>
  42. *
  43. * <p> The System property OtpConnection.trace can be used to change
  44. * the initial trace level setting for all connections. Normally the
  45. * initial trace level is 0 and connections are not traced unless
  46. * {@link #setTraceLevel setTraceLevel()} is used to change the
  47. * setting for a particular connection. OtpConnection.trace can be
  48. * used to turn on tracing by default for all connections. </p>
  49. **/
  50. public abstract class AbstractConnection
  51. {
  52. static AbstractConnection()
  53. {
  54. random = new System.Random();
  55. }
  56. protected internal const int headerLen = 2048; // more than enough
  57. private const int defaultMaxPayloadLength = 128 * 1024 * 1024; // max size of the payload sent by peer
  58. protected internal static readonly byte passThrough = 0x70;
  59. // MD5 challenge messsage tags
  60. protected internal const int ChallengeReply = 'r';
  61. protected internal const int ChallengeAck = 'a';
  62. protected internal const int ChallengeStatus = 's';
  63. private bool done = false;
  64. protected internal bool connected = false; // connection status
  65. protected internal System.Net.Sockets.TcpClient socket; // communication channel
  66. protected internal OtpPeer peer; // who are we connected to
  67. protected internal OtpLocalNode self; // this nodes id
  68. internal System.String name; // local name of this connection
  69. protected string auth_cookie;
  70. private long sentBytes; // Total number of bytes sent
  71. private long receivedBytes; // Total number of bytes received
  72. private long sentMsgs; // Total number of messages sent
  73. private long receivedMsgs; // Total number of messages received
  74. private int maxPayloadLength;
  75. protected internal bool cookieOk = false; // already checked the cookie for this connection
  76. protected internal bool sendCookie = true; // Send cookies in messages?
  77. protected internal System.Threading.Thread thread;
  78. // tracelevel constants
  79. /*
  80. * <p> Set the trace level for this connection. Normally tracing is
  81. * off by default unless System property OtpConnection.trace was
  82. * set. </p>
  83. *
  84. * <p> The following levels are valid: 0 turns off tracing
  85. * completely, 1 shows ordinary send and receive messages, 2 shows
  86. * control messages such as link and unlink, 3 shows handshaking at
  87. * connection setup, and 4 shows communication with Epmd. Each level
  88. * includes the information shown by the lower ones. </p>
  89. *
  90. **/
  91. public static OtpTrace.Type traceLevel = OtpTrace.traceLevel;
  92. private static int _connectTimeout = 5000;
  93. public static int ConnectTimeout
  94. {
  95. get { return _connectTimeout; }
  96. set { _connectTimeout = value; }
  97. }
  98. /// <summary>
  99. /// Total number of bytes sent through connection
  100. /// </summary>
  101. public long SentBytes { get { return sentBytes; } }
  102. /// <summary>
  103. /// Total number of bytes received from connection
  104. /// </summary>
  105. public long ReceivedBytes { get { return receivedBytes; } }
  106. /// <summary>
  107. /// Total number of messages sent through connection
  108. /// </summary>
  109. public long SentMsgs { get { return sentMsgs; } }
  110. /// <summary>
  111. /// Total number of messages received from connection
  112. /// </summary>
  113. public long ReceivedMsgs { get { return receivedMsgs; } }
  114. /// <summary>
  115. /// Max size of the message accepted from the peer.
  116. /// The connection will be closed if a message is received of size greater than this.
  117. /// </summary>
  118. public int MaxPayloadLength
  119. {
  120. get { return maxPayloadLength; }
  121. set { maxPayloadLength = value; }
  122. }
  123. public enum Operation { Read, Write };
  124. /// <summary>
  125. /// Delegate called on read/write from socket
  126. /// </summary>
  127. public delegate void OnReadWriteDelegate(AbstractConnection con, Operation op,
  128. long lastBytes, long totalBytes, long totalMsgs);
  129. private OnReadWriteDelegate onReadWrite = null;
  130. /// <summary>
  131. /// Delegate invoked on read/write from socket
  132. /// </summary>
  133. public OnReadWriteDelegate OnReadWrite
  134. {
  135. get { return onReadWrite; }
  136. set { onReadWrite = value; }
  137. }
  138. protected internal static System.Random random = null;
  139. private AbstractConnection(OtpLocalNode self, OtpPeer peer, System.Net.Sockets.TcpClient s, string cookie)
  140. {
  141. this.peer = peer;
  142. this.self = self;
  143. this.socket = s;
  144. this.auth_cookie = cookie ?? self._cookie;
  145. this.sentBytes = 0;
  146. this.receivedBytes = 0;
  147. this.maxPayloadLength = defaultMaxPayloadLength;
  148. }
  149. /*
  150. * Accept an incoming connection from a remote node. Used by {@link
  151. * OtpSelf#accept() OtpSelf.accept()} to create a connection
  152. * based on data received when handshaking with the peer node, when
  153. * the remote node is the connection intitiator.
  154. *
  155. * @exception C#.io.IOException if it was not possible to connect to the peer.
  156. * @exception OtpAuthException if handshake resulted in an authentication error
  157. */
  158. protected internal AbstractConnection(OtpLocalNode self, System.Net.Sockets.TcpClient s)
  159. : this(self, new OtpPeer(), s, null)
  160. {
  161. this.socket.NoDelay = true;
  162. // Use keepalive timer
  163. this.socket.Client.SetSocketOption(
  164. System.Net.Sockets.SocketOptionLevel.Socket,
  165. System.Net.Sockets.SocketOptionName.KeepAlive, true);
  166. // Close socket gracefully
  167. this.socket.Client.SetSocketOption(
  168. System.Net.Sockets.SocketOptionLevel.Socket,
  169. System.Net.Sockets.SocketOptionName.DontLinger, true);
  170. //this.socket.ReceiveTimeout = 5000;
  171. if (traceLevel >= OtpTrace.Type.handshakeThreshold)
  172. {
  173. OtpTrace.TraceEvent("<- ACCEPT FROM " +
  174. System.Net.IPAddress.Parse(s.Client.RemoteEndPoint.ToString()).ToString() + ":" +
  175. (s.Client.RemoteEndPoint as System.Net.IPEndPoint).Port.ToString());
  176. }
  177. // get his info
  178. recvName(this.peer);
  179. // now find highest common dist value
  180. if ((peer._proto != self._proto) || (self._distHigh < peer._distLow) || (self._distLow > peer._distHigh))
  181. {
  182. close();
  183. throw new System.IO.IOException("No common protocol found - cannot accept connection");
  184. }
  185. // highest common version: min(peer.distHigh, self.distHigh)
  186. peer.distChoose = (peer._distHigh > self._distHigh?self._distHigh:peer._distHigh);
  187. doAccept();
  188. this.name = peer.node();
  189. }
  190. /*
  191. * Intiate and open a connection to a remote node.
  192. *
  193. * @exception C#.io.IOException if it was not possible to connect to the peer.
  194. * @exception OtpAuthException if handshake resulted in an authentication error.
  195. */
  196. protected internal AbstractConnection(OtpLocalNode self, OtpPeer other)
  197. : this(self, other, self.cookie())
  198. {
  199. }
  200. /*
  201. * Intiate and open a connection to a remote node.
  202. *
  203. * @exception C#.io.IOException if it was not possible to connect to the peer.
  204. * @exception OtpAuthException if handshake resulted in an authentication error.
  205. */
  206. protected internal AbstractConnection(OtpLocalNode self, OtpPeer other, string cookie)
  207. : this(self, other, null, cookie)
  208. {
  209. // now get a connection between the two...
  210. int port = OtpEpmd.lookupPort(peer);
  211. // now find highest common dist value
  212. if ((peer._proto != self._proto) || (self._distHigh < peer._distLow) || (self._distLow > peer._distHigh))
  213. {
  214. throw new System.IO.IOException("No common protocol found - cannot connect");
  215. }
  216. // highest common version: min(peer.distHigh, self.distHigh)
  217. peer.distChoose = (peer._distHigh > self._distHigh ? self._distHigh : peer._distHigh);
  218. doConnect(port);
  219. this.name = peer.node();
  220. this.connected = true;
  221. }
  222. /*
  223. * Deliver communication exceptions to the recipient.
  224. **/
  225. public abstract void deliver(System.Exception e);
  226. /*
  227. * Deliver messages to the recipient.
  228. **/
  229. public abstract void deliver(OtpMsg msg);
  230. /*
  231. * Send a pre-encoded message to a named process on a remote node.
  232. *
  233. * @param dest the name of the remote process.
  234. * @param payload the encoded message to send.
  235. *
  236. * @exception C#.io.IOException if the connection is not active or
  237. * a communication error occurs.
  238. **/
  239. protected internal virtual void sendBuf(Erlang.Pid from, System.String dest, OtpOutputStream payload)
  240. {
  241. if (!connected)
  242. {
  243. throw new System.IO.IOException("Not connected");
  244. }
  245. OtpOutputStream header = new OtpOutputStream(headerLen);
  246. // preamble: 4 byte length + "passthrough" tag + version
  247. header.write4BE(0); // reserve space for length
  248. header.write1(passThrough);
  249. header.write1(OtpExternal.versionTag);
  250. // header info
  251. header.write_tuple_head(4);
  252. header.write_long((long)OtpMsg.Tag.regSendTag);
  253. header.write_any(from);
  254. if (sendCookie)
  255. header.write_atom(auth_cookie);
  256. else
  257. header.write_atom("");
  258. header.write_atom(dest);
  259. // version for payload
  260. header.write1(OtpExternal.versionTag);
  261. // fix up length in preamble
  262. header.poke4BE(0, header.count() + payload.count() - 4);
  263. do_send(header, payload);
  264. }
  265. /*
  266. * Send a pre-encoded message to a process on a remote node.
  267. *
  268. * @param dest the Erlang PID of the remote process.
  269. * @param msg the encoded message to send.
  270. *
  271. * @exception C#.io.IOException if the connection is not active
  272. * or a communication error occurs.
  273. **/
  274. protected internal virtual void sendBuf(Erlang.Pid from, Erlang.Pid dest, OtpOutputStream payload)
  275. {
  276. if (!connected)
  277. {
  278. throw new System.IO.IOException("Not connected");
  279. }
  280. OtpOutputStream header = new OtpOutputStream(headerLen);
  281. // preamble: 4 byte length + "passthrough" tag + version
  282. header.write4BE(0); // reserve space for length
  283. header.write1(passThrough);
  284. header.write1(OtpExternal.versionTag);
  285. // header info
  286. header.write_tuple_head(3);
  287. header.write_long((long)OtpMsg.Tag.sendTag);
  288. if (sendCookie)
  289. header.write_atom(auth_cookie);
  290. else
  291. header.write_atom("");
  292. header.write_any(dest);
  293. // version for payload
  294. header.write1(OtpExternal.versionTag);
  295. // fix up length in preamble
  296. header.poke4BE(0, header.count() + payload.count() - 4);
  297. do_send(header, payload);
  298. }
  299. /*Send an auth error to peer because he sent a bad cookie.
  300. * The auth error uses his cookie (not revealing ours).
  301. * This is just like send_reg otherwise
  302. */
  303. private void cookieError(OtpLocalNode local, Erlang.Atom cookie)
  304. {
  305. try
  306. {
  307. OtpOutputStream header = new OtpOutputStream(headerLen);
  308. // preamble: 4 byte length + "passthrough" tag + version
  309. header.write4BE(0); // reserve space for length
  310. header.write1(passThrough);
  311. header.write1(OtpExternal.versionTag);
  312. header.write_tuple_head(4);
  313. header.write_long((long)OtpMsg.Tag.regSendTag);
  314. header.write_any(local.createPid()); // disposable pid
  315. header.write_atom(cookie.atomValue()); // important: his cookie, not mine...
  316. header.write_atom("auth");
  317. // version for payload
  318. header.write1(OtpExternal.versionTag);
  319. // the payload
  320. // the no_auth message (copied from Erlang) Don't change this (Erlang will crash)
  321. // {$gen_cast, {print, "~n** Unauthorized cookie ~w **~n", [foo@aule]}}
  322. Erlang.Object[] msg = new Erlang.Object[2];
  323. Erlang.Object[] msgbody = new Erlang.Object[3];
  324. msgbody[0] = new Erlang.Atom("print");
  325. msgbody[1] = new Erlang.String("~n** Bad cookie sent to " + local + " **~n");
  326. // Erlang will crash and burn if there is no third argument here...
  327. msgbody[2] = new Erlang.List(); // empty list
  328. msg[0] = new Erlang.Atom("$gen_cast");
  329. msg[1] = new Erlang.Tuple(msgbody);
  330. OtpOutputStream payload = new OtpOutputStream(new Erlang.Tuple(msg));
  331. // fix up length in preamble
  332. header.poke4BE(0, header.count() + payload.count() - 4);
  333. try
  334. {
  335. do_send(header, payload);
  336. }
  337. catch (System.IO.IOException)
  338. {
  339. } // ignore
  340. }
  341. finally
  342. {
  343. close();
  344. throw new OtpAuthException("Remote cookie not authorized: " + cookie.atomValue());
  345. }
  346. }
  347. // link to pid
  348. /*
  349. * Create a link between the local node and the specified process on
  350. * the remote node. If the link is still active when the remote
  351. * process terminates, an exit signal will be sent to this
  352. * connection. Use {@link #sendUnlink unlink()} to remove the link.
  353. *
  354. * @param dest the Erlang PID of the remote process.
  355. *
  356. * @exception C#.io.IOException if the connection is not active
  357. * or a communication error occurs.
  358. **/
  359. protected internal virtual void sendLink(Erlang.Pid from, Erlang.Pid dest)
  360. {
  361. if (!connected)
  362. {
  363. throw new System.IO.IOException("Not connected");
  364. }
  365. OtpOutputStream header = new OtpOutputStream(headerLen);
  366. // preamble: 4 byte length + "passthrough" tag
  367. header.write4BE(0); // reserve space for length
  368. header.write1(passThrough);
  369. header.write1(OtpExternal.versionTag);
  370. // header
  371. header.write_tuple_head(3);
  372. header.write_long((long)OtpMsg.Tag.linkTag);
  373. header.write_any(from);
  374. header.write_any(dest);
  375. // fix up length in preamble
  376. header.poke4BE(0, header.count() - 4);
  377. do_send(header);
  378. }
  379. /*
  380. * Remove a link between the local node and the specified process on
  381. * the remote node. This method deactivates links created with
  382. * {@link #sendLink link()}.
  383. *
  384. * @param dest the Erlang PID of the remote process.
  385. *
  386. * @exception C#.io.IOException if the connection is not active or
  387. * a communication error occurs.
  388. **/
  389. protected internal virtual void sendUnlink(Erlang.Pid from, Erlang.Pid dest)
  390. {
  391. if (!connected)
  392. {
  393. throw new System.IO.IOException("Not connected");
  394. }
  395. OtpOutputStream header = new OtpOutputStream(headerLen);
  396. // preamble: 4 byte length + "passthrough" tag
  397. header.write4BE(0); // reserve space for length
  398. header.write1(passThrough);
  399. header.write1(OtpExternal.versionTag);
  400. // header
  401. header.write_tuple_head(3);
  402. header.write_long((long)OtpMsg.Tag.unlinkTag);
  403. header.write_any(from);
  404. header.write_any(dest);
  405. // fix up length in preamble
  406. header.poke4BE(0, header.count() - 4);
  407. do_send(header);
  408. }
  409. /*used internally when "processes" terminate */
  410. protected internal virtual void sendExit(Erlang.Pid from, Erlang.Pid dest, System.String reason)
  411. {
  412. sendExit((int)OtpMsg.Tag.exitTag, from, dest, reason);
  413. }
  414. /*
  415. * Send an exit signal to a remote process.
  416. *
  417. * @param dest the Erlang PID of the remote process.
  418. * @param reason a string describing the exit reason.
  419. *
  420. * @exception C#.io.IOException if the connection is not active or
  421. * a communication error occurs.
  422. **/
  423. protected internal virtual void sendExit2(Erlang.Pid from, Erlang.Pid dest, System.String reason)
  424. {
  425. sendExit((int)OtpMsg.Tag.exit2Tag, from, dest, reason);
  426. }
  427. private void sendExit(int tag, Erlang.Pid from, Erlang.Pid dest, System.String reason)
  428. {
  429. if (!connected)
  430. {
  431. throw new System.IO.IOException("Not connected");
  432. }
  433. OtpOutputStream header = new OtpOutputStream(headerLen);
  434. // preamble: 4 byte length + "passthrough" tag
  435. header.write4BE(0); // reserve space for length
  436. header.write1(passThrough);
  437. header.write1(OtpExternal.versionTag);
  438. // header
  439. header.write_tuple_head(4);
  440. header.write_long(tag);
  441. header.write_any(from);
  442. header.write_any(dest);
  443. header.write_string(reason);
  444. // fix up length in preamble
  445. header.poke4BE(0, header.count() - 4);
  446. do_send(header);
  447. }
  448. public virtual void Start()
  449. {
  450. if (!connected)
  451. {
  452. deliver(new System.IO.IOException("Not connected"));
  453. return ;
  454. }
  455. OtpInputStream ibuf;
  456. Erlang.Object traceobj;
  457. int len;
  458. byte[] header = new byte[4];
  459. byte[] tock = new byte[]{0, 0, 0, 0};
  460. byte[] payloadBuf = new byte[1024 * 1024];
  461. try
  462. {
  463. while (!done)
  464. {
  465. // don't return until we get a real message
  466. // or a failure of some kind (e.g. EXIT)
  467. // read length and read buffer must be atomic!
  468. do
  469. {
  470. // read 4 bytes - get length of incoming packet
  471. // socket.getInputStream().read(lbuf);
  472. int n;
  473. if ((n = readSock(socket, header, header.Length, false)) < header.Length)
  474. throw new System.Exception(String.Format("Read {0} out of {1} bytes!", n, header.Length));
  475. len = OtpInputStream.read4BE(header);
  476. // received tick? send tock!
  477. if (len == 0)
  478. lock (this)
  479. {
  480. System.Byte[] temp_bytearray;
  481. temp_bytearray = tock;
  482. if (socket != null)
  483. ((System.IO.Stream)socket.GetStream()).Write(temp_bytearray, 0, temp_bytearray.Length);
  484. }
  485. }
  486. while (len == 0); // tick_loop
  487. if (len > maxPayloadLength)
  488. throw new System.Exception(
  489. String.Format("Message size too long (max={0}, got={1})", maxPayloadLength, len));
  490. // got a real message (maybe) - read len bytes
  491. byte[] tmpbuf = new byte[len]; // len > payloadBuf.Length ? new byte[len] : payloadBuf;
  492. // i = socket.getInputStream().read(tmpbuf);
  493. int m = readSock(socket, tmpbuf, len, true);
  494. if (m != len)
  495. throw new System.Exception(String.Format("Read {0} out of {1} bytes!", m, len));
  496. ibuf = new OtpInputStream(tmpbuf, 0, len);
  497. if (ibuf.read1() != passThrough)
  498. {
  499. goto receive_loop_brk;
  500. }
  501. // got a real message (really)
  502. Erlang.Atom reason = null;
  503. Erlang.Atom cookie = null;
  504. Erlang.Object tmp = null;
  505. Erlang.Tuple head = null;
  506. Erlang.Atom toName;
  507. Erlang.Pid to;
  508. Erlang.Pid from;
  509. Erlang.Ref eref;
  510. // decode the header
  511. tmp = ibuf.read_any();
  512. if (!(tmp is Erlang.Tuple))
  513. goto receive_loop_brk;
  514. head = (Erlang.Tuple)tmp;
  515. if (!(head.elementAt(0) is Erlang.Long))
  516. {
  517. goto receive_loop_brk;
  518. }
  519. // lets see what kind of message this is
  520. OtpMsg.Tag tag = (OtpMsg.Tag)head.elementAt(0).longValue();
  521. switch (tag)
  522. {
  523. case OtpMsg.Tag.sendTag:
  524. case OtpMsg.Tag.sendTTTag:
  525. // { SEND, Cookie, ToPid, TraceToken }
  526. if (!cookieOk)
  527. {
  528. // we only check this once, he can send us bad cookies later if he likes
  529. if (!(head.elementAt(1) is Erlang.Atom))
  530. {
  531. goto receive_loop_brk;
  532. }
  533. cookie = (Erlang.Atom)head.elementAt(1);
  534. if (sendCookie)
  535. {
  536. if (!cookie.atomValue().Equals(auth_cookie))
  537. {
  538. cookieError(self, cookie);
  539. }
  540. }
  541. else
  542. {
  543. if (!cookie.atomValue().Equals(""))
  544. {
  545. cookieError(self, cookie);
  546. }
  547. }
  548. cookieOk = true;
  549. }
  550. if (traceLevel >= OtpTrace.Type.sendThreshold)
  551. {
  552. OtpTrace.TraceEvent("<- " + headerType(head) + " " + head.ToString());
  553. /*show received payload too */
  554. long mark = ibuf.Position;
  555. traceobj = ibuf.read_any();
  556. if (traceobj != null)
  557. OtpTrace.TraceEvent(" " + traceobj.ToString());
  558. else
  559. OtpTrace.TraceEvent(" (null)");
  560. ibuf.Seek(mark, System.IO.SeekOrigin.Begin);
  561. }
  562. to = (Erlang.Pid)(head.elementAt(2));
  563. deliver(new OtpMsg(to, ibuf));
  564. break;
  565. case OtpMsg.Tag.regSendTag:
  566. case OtpMsg.Tag.regSendTTTag:
  567. // { REG_SEND, FromPid, Cookie, ToName, TraceToken }
  568. if (!cookieOk)
  569. {
  570. // we only check this once, he can send us bad cookies later if he likes
  571. if (!(head.elementAt(2) is Erlang.Atom))
  572. {
  573. goto receive_loop_brk;
  574. }
  575. cookie = (Erlang.Atom)head.elementAt(2);
  576. if (sendCookie)
  577. {
  578. if (!cookie.atomValue().Equals(auth_cookie))
  579. {
  580. cookieError(self, cookie);
  581. }
  582. }
  583. else
  584. {
  585. if (!cookie.atomValue().Equals(""))
  586. {
  587. cookieError(self, cookie);
  588. }
  589. }
  590. cookieOk = true;
  591. }
  592. if (traceLevel >= OtpTrace.Type.sendThreshold)
  593. {
  594. OtpTrace.TraceEvent("<- " + headerType(head) + " " + head.ToString());
  595. /*show received payload too */
  596. long mark = ibuf.Position;
  597. traceobj = ibuf.read_any();
  598. if (traceobj != null)
  599. OtpTrace.TraceEvent(" " + traceobj.ToString());
  600. else
  601. OtpTrace.TraceEvent(" (null)");
  602. ibuf.Seek(mark, System.IO.SeekOrigin.Begin);
  603. }
  604. from = (Erlang.Pid)(head.elementAt(1));
  605. toName = (Erlang.Atom)(head.elementAt(3));
  606. deliver(new OtpMsg(from, toName.atomValue(), ibuf));
  607. break;
  608. case OtpMsg.Tag.exitTag:
  609. case OtpMsg.Tag.exit2Tag:
  610. // { EXIT2, FromPid, ToPid, Reason }
  611. if (!(head.elementAt(3) is Erlang.Atom))
  612. {
  613. goto receive_loop_brk;
  614. }
  615. if (traceLevel >= OtpTrace.Type.ctrlThreshold)
  616. {
  617. OtpTrace.TraceEvent("<- " + headerType(head) + " " + head.ToString());
  618. }
  619. from = (Erlang.Pid)(head.elementAt(1));
  620. to = (Erlang.Pid)(head.elementAt(2));
  621. reason = (Erlang.Atom)head.elementAt(3);
  622. deliver(new OtpMsg(tag, from, to, reason));
  623. break;
  624. case OtpMsg.Tag.exitTTTag:
  625. case OtpMsg.Tag.exit2TTTag:
  626. // { EXIT2, FromPid, ToPid, TraceToken, Reason }
  627. // as above, but bifferent element number
  628. if (!(head.elementAt(4) is Erlang.Atom))
  629. {
  630. goto receive_loop_brk;
  631. }
  632. if (traceLevel >= OtpTrace.Type.ctrlThreshold)
  633. {
  634. OtpTrace.TraceEvent("<- " + headerType(head) + " " + head.ToString());
  635. }
  636. from = (Erlang.Pid)(head.elementAt(1));
  637. to = (Erlang.Pid)(head.elementAt(2));
  638. reason = (Erlang.Atom)head.elementAt(4);
  639. deliver(new OtpMsg(tag, from, to, reason));
  640. break;
  641. case OtpMsg.Tag.linkTag:
  642. case OtpMsg.Tag.unlinkTag:
  643. // { UNLINK, FromPid, ToPid}
  644. if (traceLevel >= OtpTrace.Type.ctrlThreshold)
  645. {
  646. OtpTrace.TraceEvent("<- " + headerType(head) + " " + head.ToString());
  647. }
  648. from = (Erlang.Pid)(head.elementAt(1));
  649. to = (Erlang.Pid)(head.elementAt(2));
  650. deliver(new OtpMsg(tag, from, to));
  651. break;
  652. // absolutely no idea what to do with these, so we ignore them...
  653. case OtpMsg.Tag.groupLeaderTag:
  654. case OtpMsg.Tag.nodeLinkTag:
  655. // { NODELINK }
  656. // (just show trace)
  657. if (traceLevel >= OtpTrace.Type.ctrlThreshold)
  658. {
  659. OtpTrace.TraceEvent("<- " + headerType(head) + " " + head.ToString());
  660. }
  661. break;
  662. case OtpMsg.Tag.monitorPTag:
  663. // {MONITOR_P, FromPid, ToProc, Ref}
  664. case OtpMsg.Tag.demonitorPTag:
  665. // {DEMONITOR_P, FromPid, ToProc, Ref}
  666. if (traceLevel >= OtpTrace.Type.ctrlThreshold)
  667. {
  668. OtpTrace.TraceEvent("<- " + headerType(head) + " " + head.ToString());
  669. }
  670. from = (Erlang.Pid)(head.elementAt(1));
  671. to = (Erlang.Pid)(head.elementAt(2));
  672. eref = (Erlang.Ref)(head.elementAt(3));
  673. deliver(new OtpMsg(tag, from, to, eref));
  674. break;
  675. case OtpMsg.Tag.monitorPexitTag:
  676. // {MONITOR_P_EXIT, FromPid, ToProc, Ref, Reason}
  677. if (traceLevel >= OtpTrace.Type.ctrlThreshold)
  678. {
  679. OtpTrace.TraceEvent("<- " + headerType(head) + " " + head.ToString());
  680. }
  681. from = (Erlang.Pid)(head.elementAt(1));
  682. to = (Erlang.Pid)(head.elementAt(2));
  683. eref = (Erlang.Ref)(head.elementAt(3));
  684. deliver(new OtpMsg(tag, from, to, eref, reason));
  685. break;
  686. default:
  687. // garbage?
  688. if (traceLevel >= OtpTrace.Type.ctrlThreshold)
  689. {
  690. OtpTrace.TraceEvent("<- Unknown tag " + headerType(head) + " " + head.ToString());
  691. }
  692. goto receive_loop_brk;
  693. }
  694. }
  695. receive_loop_brk: ;
  696. // end receive_loop
  697. // this section reachable only with break
  698. // we have received garbage from peer
  699. deliver(new Erlang.Exit("Remote is sending garbage"));
  700. }
  701. catch (OtpAuthException e)
  702. {
  703. deliver(e);
  704. }
  705. catch (Erlang.Exception e)
  706. {
  707. OtpTrace.TraceEvent(e.ToString());
  708. deliver(new Erlang.Exit("Remote is sending garbage: " + e.ToString()));
  709. }
  710. catch (System.Exception e)
  711. {
  712. deliver(new Erlang.Exit("Remote has closed connection: " + e.ToString()));
  713. }
  714. finally
  715. {
  716. close();
  717. OtpTrace.TraceEvent("exit connection "+System.Threading.Thread.CurrentThread.Name);
  718. }
  719. }
  720. /*
  721. * Close the connection to the remote node.
  722. **/
  723. public virtual void close()
  724. {
  725. done = true;
  726. connected = false;
  727. lock(this)
  728. {
  729. try
  730. {
  731. if (socket != null)
  732. {
  733. if (traceLevel >= OtpTrace.Type.ctrlThreshold)
  734. {
  735. OtpTrace.TraceEvent("-> CLOSE");
  736. }
  737. socket.Close();
  738. //thread.Interrupt();
  739. }
  740. }
  741. catch (System.Net.Sockets.SocketException)
  742. {
  743. /*ignore socket close errors */
  744. }
  745. finally
  746. {
  747. socket = null;
  748. }
  749. }
  750. }
  751. ~AbstractConnection()
  752. {
  753. close();
  754. }
  755. /*
  756. * Determine if the connection is still alive. Note that this method
  757. * only reports the status of the connection, and that it is
  758. * possible that there are unread messages waiting in the receive
  759. * queue.
  760. *
  761. * @return true if the connection is alive.
  762. **/
  763. public virtual bool isConnected()
  764. {
  765. return connected;
  766. }
  767. /*
  768. * Send an RPC request to the remote Erlang node. This convenience
  769. * function creates the following message and sends it to 'rex' on
  770. * the remote node:
  771. *
  772. * <pre>
  773. * { self, { call, Mod, Fun, Args, user }}
  774. * </pre>
  775. *
  776. * <p> Note that this method has unpredicatble results if the remote
  777. * node is not an Erlang node. </p>
  778. *
  779. * @param mod the name of the Erlang module containing the function to be called.
  780. * @param fun the name of the function to call.
  781. * @param args a list of Erlang terms, to be used as arguments to the function.
  782. *
  783. * @exception C#.io.IOException if the connection is not active
  784. * or a communication error occurs.
  785. **/
  786. public virtual void sendRPC(Erlang.Pid from, System.String mod, System.String fun, Erlang.List args)
  787. {
  788. Erlang.Object rpc = encodeRPC(from, mod, fun, args, new Erlang.Atom("user"));
  789. sendBuf(from, "rex", new OtpOutputStream(rpc));
  790. }
  791. /*
  792. * Send an RPC cast request to the remote Erlang node. This convenience
  793. * function creates the following message and sends it to 'rex' on
  794. * the remote node:
  795. *
  796. * <pre>
  797. * { self, { cast, Mod, Fun, Args, user }}
  798. * </pre>
  799. *
  800. * No reply is delivered back
  801. * <p> Note that this method has unpredicatble results if the remote
  802. * node is not an Erlang node. </p>
  803. *
  804. * @param mod the name of the Erlang module containing the function to be called.
  805. * @param fun the name of the function to call.
  806. * @param args a list of Erlang terms, to be used as arguments to the function.
  807. *
  808. * @exception C#.io.IOException if the connection is not active
  809. * or a communication error occurs.
  810. **/
  811. public virtual void sendRPCcast(Erlang.Pid from, string mod, string fun, Erlang.List args)
  812. {
  813. Erlang.Object rpc = encodeRPCcast(from, mod, fun, args, new Erlang.Atom("user"));
  814. sendBuf(from, "rex", new OtpOutputStream(rpc));
  815. }
  816. internal static Erlang.Tuple encodeRPC(
  817. Erlang.Pid from, string mod, string fun, Erlang.List args, Erlang.Object gleader)
  818. {
  819. return encodeRPC(from, new Erlang.Atom(mod), new Erlang.Atom(fun), args, gleader);
  820. }
  821. internal static Erlang.Tuple encodeRPCcast(
  822. Erlang.Pid from, string mod, string fun, Erlang.List args, Erlang.Object gleader)
  823. {
  824. return encodeRPCcast(from, new Erlang.Atom(mod), new Erlang.Atom(fun), args, gleader);
  825. }
  826. internal static Erlang.Tuple encodeRPC(
  827. Erlang.Pid from, Erlang.Atom mod, Erlang.Atom fun, Erlang.List args, Erlang.Object gleader)
  828. {
  829. /*{self, { cast, Mod, Fun, Args, user}} */
  830. return new Erlang.Tuple(
  831. from,
  832. new Erlang.Tuple(new Erlang.Atom("call"), mod, fun, args, gleader)
  833. );
  834. }
  835. internal static Erlang.Tuple encodeRPCcast(
  836. Erlang.Pid from, Erlang.Atom mod, Erlang.Atom fun, Erlang.List args, Erlang.Object gleader)
  837. {
  838. /*{'$gen_cast', { cast, Mod, Fun, Args, user}} */
  839. return new Erlang.Tuple(
  840. new Erlang.Atom("$gen_cast"),
  841. new Erlang.Tuple(new Erlang.Atom("cast"), mod, fun, args, gleader));
  842. }
  843. internal static Erlang.Object decodeRPC(Erlang.Object msg)
  844. {
  845. if (msg is Erlang.Tuple)
  846. {
  847. Erlang.Tuple t = (Erlang.Tuple)msg;
  848. if (t.arity() == 2)
  849. {
  850. Erlang.Atom rex = t[0] as Erlang.Atom;
  851. if (rex != null && rex.atomValue() == "rex")
  852. return t[1];
  853. // obs: second element
  854. }
  855. }
  856. return null;
  857. }
  858. internal static string decodeIO(Erlang.Object msg)
  859. {
  860. if (msg is Erlang.Tuple)
  861. {
  862. Erlang.Tuple t = (Erlang.Tuple)msg;
  863. if (t.arity() == 4)
  864. {
  865. Erlang.Atom ios = t[0] as Erlang.Atom;
  866. Erlang.Tuple data = t[3] as Erlang.Tuple;
  867. if (ios != null && ios.atomValue() == "io_request" && data != null)
  868. {
  869. if ((data[0] as Erlang.Atom).atomValue() == "put_chars")
  870. {
  871. if (data[1] is Erlang.String)
  872. return (data[1] as Erlang.String).stringValue();
  873. else if (data[1] is Erlang.Binary)
  874. {
  875. byte[] bin = (data[1] as Erlang.Binary).binaryValue();
  876. char[] s = new char[bin.Length];
  877. bin.CopyTo(s, 0);
  878. return new string(s);
  879. }
  880. else
  881. return data[1].ToString();
  882. }
  883. }
  884. }
  885. }
  886. return null;
  887. }
  888. // used by send and send_reg (message types with payload)
  889. protected internal virtual void do_send(OtpOutputStream header, OtpOutputStream payload)
  890. {
  891. lock(this)
  892. {
  893. try
  894. {
  895. if (traceLevel >= OtpTrace.Type.sendThreshold)
  896. {
  897. // Need to decode header and output buffer to show trace message!
  898. // First make OtpInputStream, then decode.
  899. try
  900. {
  901. if (traceLevel >= OtpTrace.Type.wireThreshold)
  902. {
  903. Erlang.Object h = (header.getOtpInputStream(5)).read_any();
  904. Erlang.Binary hb = header.ToBinary();
  905. Erlang.Binary ob = payload.ToBinary();
  906. System.Text.StringBuilder s = new System.Text.StringBuilder();
  907. s.AppendFormat("-> {0} {1} (header_sz={2}, msg_sz={3})\n" +
  908. " Header: {4}\n" +
  909. " Msg: {5}", headerType(h), h.ToString(), hb.size(), ob.size(),
  910. hb.ToBinaryString(), ob.ToBinaryString());
  911. OtpTrace.TraceEvent(s.ToString());
  912. h = null;
  913. hb = null;
  914. ob = null;
  915. }
  916. else
  917. {
  918. Erlang.Object h = (header.getOtpInputStream(5)).read_any();
  919. OtpTrace.TraceEvent("-> " + headerType(h) + " " + h.ToString());
  920. Erlang.Object o = (payload.getOtpInputStream(0)).read_any();
  921. OtpTrace.TraceEvent(" " + o.ToString());
  922. h = null;
  923. o = null;
  924. }
  925. }
  926. catch (Erlang.Exception e)
  927. {
  928. OtpTrace.TraceEvent(" " + "can't decode output buffer:" + e);
  929. }
  930. }
  931. header.writeTo((System.IO.Stream) socket.GetStream());
  932. payload.writeTo((System.IO.Stream) socket.GetStream());
  933. long written = header.count() + payload.count();
  934. sentBytes += written;
  935. sentMsgs++;
  936. if (onReadWrite != null)
  937. onReadWrite(this, Operation.Write, written, sentBytes, sentMsgs);
  938. }
  939. catch (System.Exception e)
  940. {
  941. close();
  942. throw e;
  943. }
  944. }
  945. }
  946. // used by the other message types
  947. protected internal virtual void do_send(OtpOutputStream header)
  948. {
  949. lock(this)
  950. {
  951. try
  952. {
  953. if (traceLevel >= OtpTrace.Type.ctrlThreshold)
  954. {
  955. try
  956. {
  957. Erlang.Object h = (header.getOtpInputStream(5)).read_any();
  958. OtpTrace.TraceEvent("-> " + headerType(h) + " " + h);
  959. }
  960. catch (Erlang.Exception e)
  961. {
  962. OtpTrace.TraceEvent(" " + "can't decode output buffer: " + e);
  963. }
  964. }
  965. header.writeTo((System.IO.Stream) socket.GetStream());
  966. }
  967. catch (System.Exception e)
  968. {
  969. close();
  970. throw e;
  971. }
  972. }
  973. }
  974. protected internal virtual System.String headerType(Erlang.Object h)
  975. {
  976. OtpMsg.Tag tag = OtpMsg.Tag.undefined;
  977. if (h is Erlang.Tuple)
  978. {
  979. tag = (OtpMsg.Tag) (((Erlang.Long) (((Erlang.Tuple) h).elementAt(0))).longValue());
  980. }
  981. switch (tag)
  982. {
  983. case OtpMsg.Tag.linkTag:
  984. return "LINK";
  985. case OtpMsg.Tag.sendTag:
  986. return "SEND";
  987. case OtpMsg.Tag.exitTag:
  988. return "EXIT";
  989. case OtpMsg.Tag.unlinkTag:
  990. return "UNLINK";
  991. case OtpMsg.Tag.nodeLinkTag:
  992. return "NODELINK";
  993. case OtpMsg.Tag.regSendTag:
  994. return "REG_SEND";
  995. case OtpMsg.Tag.groupLeaderTag:
  996. return "GROUP_LEADER";
  997. case OtpMsg.Tag.exit2Tag:
  998. return "EXIT2";
  999. case OtpMsg.Tag.sendTTTag:
  1000. return "SEND_TT";
  1001. case OtpMsg.Tag.exitTTTag:
  1002. return "EXIT_TT";
  1003. case OtpMsg.Tag.regSendTTTag:
  1004. return "REG_SEND_TT";
  1005. case OtpMsg.Tag.exit2TTTag:
  1006. return "EXIT2_TT";
  1007. case OtpMsg.Tag.monitorPTag:
  1008. return "MONITOR_P";
  1009. case OtpMsg.Tag.demonitorPTag:
  1010. return "DEMONITOR_P";
  1011. case OtpMsg.Tag.monitorPexitTag:
  1012. return "MONITOR_P_EXIT";
  1013. }
  1014. return "(unknown type)";
  1015. }
  1016. /*this method now throws exception if we don't get full read */
  1017. protected internal virtual int readSock(System.Net.Sockets.TcpClient s, byte[] b, int sz, bool readingPayload)
  1018. {
  1019. int got = 0;
  1020. int len = sz;
  1021. int i;
  1022. System.IO.Stream is_Renamed = null;
  1023. lock (this)
  1024. {
  1025. if (s == null)
  1026. {
  1027. throw new System.IO.IOException("expected " + len + " bytes, socket was closed");
  1028. }
  1029. is_Renamed = (System.IO.Stream)s.GetStream();
  1030. }
  1031. while (got < len && is_Renamed.CanRead)
  1032. {
  1033. i = is_Renamed.Read(b, got, len - got);
  1034. if (i < 0)
  1035. {
  1036. throw new System.IO.IOException("Expected " + len + " bytes, got EOF after " + got + " bytes");
  1037. }
  1038. else if (i == 0)
  1039. {
  1040. throw new System.IO.IOException("Remote connection closed");
  1041. }
  1042. got += i;
  1043. }
  1044. receivedBytes += got;
  1045. if (readingPayload)
  1046. {
  1047. receivedMsgs++;
  1048. if (onReadWrite != null)
  1049. onReadWrite(this, Operation.Read, got + 4 /* header len */, receivedBytes, receivedMsgs)

Large files files are truncated, but you can click here to view the full file