internal override void OnTransfer(Delivery delivery, Transfer transfer, ByteBuffer buffer) { if (delivery == null) { delivery = this.deliveryCurrent; AmqpBitConverter.WriteBytes(delivery.Buffer, buffer.Buffer, buffer.Offset, buffer.Length); } else { buffer.AddReference(); delivery.Buffer = buffer; lock (this.ThisLock) { this.OnDelivery(transfer.DeliveryId); } } if (!transfer.More) { this.deliveryCurrent = null; delivery.Message = Message.Decode(delivery.Buffer); Waiter waiter; MessageCallback callback = this.onMessage; lock (this.ThisLock) { waiter = (Waiter)this.waiterList.First; if (waiter != null) { this.waiterList.Remove(waiter); } else if (callback == null) { this.receivedMessages.Add(new MessageNode() { Message = delivery.Message }); return; } } while (waiter != null) { if (waiter.Signal(delivery.Message)) { return; } lock (this.ThisLock) { waiter = (Waiter)this.waiterList.First; if (waiter != null) { this.waiterList.Remove(waiter); } else if (callback == null) { this.receivedMessages.Add(new MessageNode() { Message = delivery.Message }); return; } } } Fx.Assert(waiter == null, "waiter must be null now"); Fx.Assert(callback != null, "callback must not be null now"); callback(this, delivery.Message); } else { this.deliveryCurrent = delivery; } }
/// <summary> /// Sets the read position. /// </summary> /// <param name="seekPosition">Position to set.</param> public void Seek(int seekPosition) { Fx.Assert(seekPosition >= 0, "seekPosition must not be negative."); Fx.Assert((this.start + seekPosition) <= this.write, "seekPosition too large."); this.read = this.start + seekPosition; }
/// <summary> /// Moves back the write position. As a result, length is decreased by size. /// </summary> /// <param name="size"></param> public void Shrink(int size) { Fx.Assert(size >= 0 && size <= this.Length, "size must be positive and not greater then length."); this.write -= size; }
/// <summary> /// Advances the write position. As a result, length is increased by size. /// </summary> /// <param name="size">Size to advance.</param> public void Append(int size) { Fx.Assert(size >= 0, "size must be positive."); Fx.Assert((this.write + size) <= this.end, "Append size too large."); this.write += size; }
/// <summary> /// Advances the read position. /// </summary> /// <param name="size">Size to advance.</param> public void Complete(int size) { Fx.Assert(size >= 0, "size must be positive."); Fx.Assert((this.read + size) <= this.write, "Complete size too large."); this.read += size; }
void Parse(string address) { // amqp[s]://user:[email protected]:port/foo/bar ParseState state = ParseState.Scheme; int startIndex = 0; for (int i = 0; i < address.Length; ++i) { switch (address[i]) { case ':': if (state == ParseState.Scheme) { this.Scheme = address.Substring(startIndex, i - startIndex); state = ParseState.Slash1; } else if (state == ParseState.User) { this.User = address.Substring(startIndex, i - startIndex); state = ParseState.Password; startIndex = i + 1; } else if (state == ParseState.Host) { this.Host = address.Substring(startIndex, i - startIndex); state = ParseState.Port; startIndex = i + 1; } else { throw new AmqpException(ErrorCode.InvalidField, Fx.Format(SRAmqp.InvalidAddressFormat)); } break; case '/': if (state == ParseState.Slash1) { state = ParseState.Slash2; } else if (state == ParseState.Slash2) { state = ParseState.User; startIndex = i + 1; } else if (state == ParseState.User || state == ParseState.Host) { this.Host = address.Substring(startIndex, i - startIndex); state = ParseState.Path; startIndex = i; } else if (state == ParseState.Port) { this.Port = int.Parse(address.Substring(startIndex, i - startIndex)); state = ParseState.Path; startIndex = i; } else if (state == ParseState.Password) { this.Host = this.User; this.User = null; this.Port = int.Parse(address.Substring(startIndex, i - startIndex)); state = ParseState.Path; startIndex = i; } break; case '@': if (state == ParseState.Password) { this.Password = address.Substring(startIndex, i - startIndex); state = ParseState.Host; startIndex = i + 1; } else { throw new AmqpException(ErrorCode.InvalidField, Fx.Format(SRAmqp.InvalidAddressFormat)); } break; default: break; } if (state == ParseState.Path) { this.Path = address.Substring(startIndex); break; } } // check state in case of no trailing slash if (state == ParseState.User || state == ParseState.Host) { this.Host = address.Substring(startIndex); } else if (state == ParseState.Password) { this.Host = this.User; this.User = null; if (startIndex < address.Length - 1) { this.Port = int.Parse(address.Substring(startIndex)); } } else if (state == ParseState.Port) { this.Port = int.Parse(address.Substring(startIndex)); } if (this.Password != null && this.Password.Length > 0) { this.Password = Uri.UnescapeDataString(this.Password); } if (this.User != null && this.User.Length > 0) { this.User = Uri.UnescapeDataString(this.User); } if (this.Host != null) { this.Host = Uri.UnescapeDataString(this.Host); } }
public void Start() { Fx.StartThread(this.PumpThread); }
internal override void OnTransfer(Delivery delivery, Transfer transfer, ByteBuffer buffer) { if (!transfer.More) { Waiter waiter; MessageCallback callback; lock (this.ThisLock) { if (delivery == null) { // multi-transfer delivery delivery = this.deliveryCurrent; this.deliveryCurrent = null; Fx.Assert(delivery != null, "Must have a delivery in the queue"); AmqpBitConverter.WriteBytes(delivery.Buffer, buffer.Buffer, buffer.Offset, buffer.Length); delivery.Message = Message.Decode(delivery.Buffer); delivery.Buffer = null; } else { // single tranfer delivery this.OnDelivery(transfer.DeliveryId); delivery.Message = Message.Decode(buffer); } callback = this.onMessage; waiter = (Waiter)this.waiterList.First; if (waiter != null) { this.waiterList.Remove(waiter); } if (waiter == null && callback == null) { this.receivedMessages.Add(new MessageNode() { Message = delivery.Message }); return; } } while (waiter != null) { if (waiter.Signal(delivery.Message)) { this.OnDeliverMessage(); return; } lock (this.ThisLock) { waiter = (Waiter)this.waiterList.First; if (waiter != null) { this.waiterList.Remove(waiter); } else if (callback == null) { this.receivedMessages.Add(new MessageNode() { Message = delivery.Message }); return; } } } Fx.Assert(waiter == null, "waiter must be null now"); Fx.Assert(callback != null, "callback must not be null now"); callback(this, delivery.Message); this.OnDeliverMessage(); } else { lock (this.ThisLock) { if (delivery == null) { delivery = this.deliveryCurrent; Fx.Assert(delivery != null, "Must have a current delivery"); AmqpBitConverter.WriteBytes(delivery.Buffer, buffer.Buffer, buffer.Offset, buffer.Length); } else { this.OnDelivery(transfer.DeliveryId); delivery.Buffer = new ByteBuffer(buffer.Length * 2, true); AmqpBitConverter.WriteBytes(delivery.Buffer, buffer.Buffer, buffer.Offset, buffer.Length); this.deliveryCurrent = delivery; } } } }
void OnFrame(ushort channel, ulong code, List fields, ByteBuffer payload) { switch (code) { case 0x10ul: // open Fx.DebugPrint(false, channel, "open", fields, "container-id", "host-name", "max-frame-size", "channel-max", "idle-time-out"); this.state |= OpenReceived; // process open.idle-time-out if exists: final value is determined by the min of the local and the remote values uint remoteValue = uint.MaxValue; if (fields.Count >= 5 && fields[4] != null) { remoteValue = (uint)fields[4]; } uint timeout = this.idleTimeout < remoteValue ? this.idleTimeout : remoteValue; if (timeout < uint.MaxValue) { timeout -= 5000; timeout = timeout > MaxIdleTimeout ? MaxIdleTimeout : timeout; this.heartBeatTimer = new Timer(onHeartBeatTimer, this, (int)timeout, (int)timeout); } break; case 0x11ul: // begin Fx.DebugPrint(false, channel, "begin", fields, "remote-channel", "next-outgoing-id", "incoming-window", "outgoing-window", "handle-max"); this.nextIncomingId = (uint)fields[1]; this.outWindow = (uint)fields[2]; this.state |= BeginReceived; break; case 0x12ul: // attach { Fx.DebugPrint(false, channel, "attach", fields, "name", "handle", "role", "snd-mode", "rcv-mode", "source", "target"); Link link = null; uint remoteHandle = (uint)fields[1]; Fx.AssertAndThrow(ErrorCode.ClientInvalidHandle, remoteHandle < this.links.Length); lock (this) { for (int i = 0; i < this.links.Length; i++) { if (this.links[i] != null && this.links[i].Name.Equals(fields[0])) { link = this.links[i]; int index = (int)~link.RemoteHandle; Fx.AssertAndThrow(ErrorCode.ClientInvalidHandle, index == i); if (index != (int)remoteHandle) { Fx.AssertAndThrow(ErrorCode.ClientHandlInUse, this.links[(int)remoteHandle] == null); this.links[(int)remoteHandle] = link; this.links[i] = null; } break; } } } Fx.AssertAndThrow(ErrorCode.ClientLinkNotFound, link != null); link.RemoteHandle = remoteHandle; link.State |= AttachReceived; link.OnAttach(fields); break; } case 0x13ul: // flow { Fx.DebugPrint(false, channel, "flow", fields, "next-in-id", "in-window", "next-out", "out-window", "handle", "dc", "credit"); uint nextIncomingId = (uint)fields[0]; uint incomingWindow = (uint)fields[1]; lock (this) { this.outWindow = incomingWindow < uint.MaxValue ? nextIncomingId + incomingWindow - this.nextOutgoingId : uint.MaxValue; } if (fields[4] != null) { Link link = this.links[(uint)fields[4]]; Fx.AssertAndThrow(ErrorCode.ClientLinkNotFound, link != null); link.OnFlow(fields); } break; } case 0x14ul: // transfer { lock (this) { this.nextOutgoingId++; if (--this.inWindow == 0) { this.inWindow = DefaultWindowSize; List flow = new List() { this.nextIncomingId, this.inWindow, this.nextOutgoingId, this.outWindow }; this.WriteFrame(0, 0, 0x13, flow); Fx.DebugPrint(true, 0, "flow", flow, "next-in-id", "in-window", "next-out", "out-window", "handle", "dc", "credit"); } } Link link = this.links[(uint)fields[0]]; Fx.AssertAndThrow(ErrorCode.ClientLinkNotFound, link != null); ((Receiver)link).OnTransfer(fields, payload); break; } case 0x15ul: // disposition { Fx.DebugPrint(false, channel, "disposition", fields, "role", "first", "last"); bool role = (bool)fields[0]; uint first = (uint)fields[1]; uint last = fields[2] == null ? first : (uint)fields[2]; for (int i = 0; i < this.links.Length; i++) { Link link = this.links[i]; if (link != null && role != link.Role) { link.OnDisposition(first, last, fields[4] as DescribedValue); } } break; } case 0x16ul: // detach { Fx.DebugPrint(false, channel, "detach", fields, "handle"); Link link = this.links[(uint)fields[0]]; Fx.AssertAndThrow(ErrorCode.ClientLinkNotFound, link != null); link.State |= DetachReceived; if ((link.State & DetachSent) == 0) { this.CloseLink(link); this.RaiseErrorEvent(link, GetError(fields, 2)); } break; } case 0x17ul: // end Fx.DebugPrint(false, channel, "end", null); this.state |= EndReceived; if ((this.state & EndSent) == 0) { this.WriteFrame(0, 0, 0x17ul, new List()); Fx.DebugPrint(true, channel, "end", null); this.ClearLinks(); } break; case 0x18ul: // close Fx.DebugPrint(false, channel, "close", null); this.state |= CloseReceived; if ((this.state & CloseSent) == 0) { this.Close(); this.RaiseErrorEvent(null, GetError(fields, 0)); } break; default: Fx.AssertAndThrow(ErrorCode.ClientInvalidCodeOnFrame, false); break; } }
/// <summary> /// Establish a connection and a session for the client. /// </summary> /// <param name="host">The network host name or IP address to connect.</param> /// <param name="port">The port to connect.</param> /// <param name="useSsl">If true, use secure socket.</param> /// <param name="userName">If set, the user name for authentication.</param> /// <param name="password">If set, the password for authentication.</param> public void Connect(string host, int port, bool useSsl, string userName, string password) { this.transport = Connect(host, port, useSsl); byte[] header = new byte[8] { (byte)'A', (byte)'M', (byte)'Q', (byte)'P', 0, 1, 0, 0 }; byte[] retHeader; if (userName != null) { header[4] = 3; this.transport.Write(header, 0, 8); Fx.DebugPrint(true, 0, "AMQP", new List { string.Concat(header[5], header[6], header[7]) }, header[4]); byte[] b1 = Encoding.UTF8.GetBytes(userName); byte[] b2 = Encoding.UTF8.GetBytes(password ?? string.Empty); byte[] b = new byte[1 + b1.Length + 1 + b2.Length]; Array.Copy(b1, 0, b, 1, b1.Length); Array.Copy(b2, 0, b, b1.Length + 2, b2.Length); List saslInit = new List() { new Symbol("PLAIN"), b }; this.WriteFrame(1, 0, 0x41, saslInit); Fx.DebugPrint(true, 0, "sasl-init", saslInit, "mechanism"); this.transport.Flush(); retHeader = this.ReadFixedSizeBuffer(8); Fx.DebugPrint(false, 0, "AMQP", new List { string.Concat(retHeader[5], retHeader[6], retHeader[7]) }, retHeader[4]); Fx.AssertAndThrow(ErrorCode.ClientInitializeHeaderCheckFailed, AreHeaderEqual(header, retHeader)); List body = this.ReadFrameBody(1, 0, 0x40); Fx.DebugPrint(false, 0, "sasl-mechanisms", body, "server-mechanisms"); Fx.AssertAndThrow(ErrorCode.ClientInitializeWrongBodyCount, body.Count > 0); Symbol[] mechanisms = GetSymbolMultiple(body[0]); Fx.AssertAndThrow(ErrorCode.ClientInitializeWrongSymbol, Array.IndexOf(mechanisms, new Symbol("PLAIN")) >= 0); body = this.ReadFrameBody(1, 0, 0x44); Fx.AssertAndThrow(ErrorCode.ClientInitializeWrongBodyCount, body.Count > 0); Fx.DebugPrint(false, 0, "sasl-outcome", body, "code"); Fx.AssertAndThrow(ErrorCode.ClientInitializeSaslFailed, body[0].Equals((byte)0)); // sasl-outcome.code = OK header[4] = 0; } this.transport.Write(header, 0, 8); Fx.DebugPrint(true, 0, "AMQP", new List { string.Concat(header[5], header[6], header[7]) }, header[4]); // perform open this.state |= OpenSent; var open = new List() { Guid.NewGuid().ToString(), this.hostName ?? host, MaxFrameSize, (ushort)0 }; this.WriteFrame(0, 0, 0x10, open); Fx.DebugPrint(true, 0, "open", open, "container-id", "host-name", "max-frame-size", "channel-max", "idle-time-out"); // perform begin this.state |= BeginSent; var begin = new List() { null, this.nextOutgoingId, this.inWindow, this.outWindow, (uint)(this.links.Length - 1) }; this.WriteFrame(0, 0, 0x11, begin); Fx.DebugPrint(true, 0, "begin", begin, "remote-channel", "next-outgoing-id", "incoming-window", "outgoing-window", "handle-max"); retHeader = this.ReadFixedSizeBuffer(8); Fx.DebugPrint(false, 0, "AMQP", new List { string.Concat(retHeader[5], retHeader[6], retHeader[7]) }, retHeader[4]); Fx.AssertAndThrow(ErrorCode.ClientInitializeHeaderCheckFailed, AreHeaderEqual(header, retHeader)); new Thread(this.PumpThread).Start(); }