113818fe824f6c06db83ff712dd501a80fc9d189
[mono.git] / mcs / class / RabbitMQ.Client / src / client / impl / Frame.cs
1 // This source code is dual-licensed under the Apache License, version
2 // 2.0, and the Mozilla Public License, version 1.1.
3 //
4 // The APL v2.0:
5 //
6 //---------------------------------------------------------------------------
7 //   Copyright (C) 2007, 2008 LShift Ltd., Cohesive Financial
8 //   Technologies LLC., and Rabbit Technologies Ltd.
9 //
10 //   Licensed under the Apache License, Version 2.0 (the "License");
11 //   you may not use this file except in compliance with the License.
12 //   You may obtain a copy of the License at
13 //
14 //       http://www.apache.org/licenses/LICENSE-2.0
15 //
16 //   Unless required by applicable law or agreed to in writing, software
17 //   distributed under the License is distributed on an "AS IS" BASIS,
18 //   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19 //   See the License for the specific language governing permissions and
20 //   limitations under the License.
21 //---------------------------------------------------------------------------
22 //
23 // The MPL v1.1:
24 //
25 //---------------------------------------------------------------------------
26 //   The contents of this file are subject to the Mozilla Public License
27 //   Version 1.1 (the "License"); you may not use this file except in
28 //   compliance with the License. You may obtain a copy of the License at
29 //   http://www.rabbitmq.com/mpl.html
30 //
31 //   Software distributed under the License is distributed on an "AS IS"
32 //   basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
33 //   License for the specific language governing rights and limitations
34 //   under the License.
35 //
36 //   The Original Code is The RabbitMQ .NET Client.
37 //
38 //   The Initial Developers of the Original Code are LShift Ltd.,
39 //   Cohesive Financial Technologies LLC., and Rabbit Technologies Ltd.
40 //
41 //   Portions created by LShift Ltd., Cohesive Financial Technologies
42 //   LLC., and Rabbit Technologies Ltd. are Copyright (C) 2007, 2008
43 //   LShift Ltd., Cohesive Financial Technologies LLC., and Rabbit
44 //   Technologies Ltd.;
45 //
46 //   All Rights Reserved.
47 //
48 //   Contributor(s): ______________________________________.
49 //
50 //---------------------------------------------------------------------------
51 using System;
52 using System.IO;
53
54 using RabbitMQ.Util;
55 using RabbitMQ.Client.Exceptions;
56
57 // We use spec version 0-9 for common constants such as frame types,
58 // error codes, and the frame end byte, since they don't vary *within
59 // the versions we support*. Obviously we may need to revisit this if
60 // that ever changes.
61 using CommonFraming = RabbitMQ.Client.Framing.v0_9;
62
63 namespace RabbitMQ.Client.Impl
64 {
65     public class Frame
66     {
67         public int m_type;
68         public int m_channel;
69         public byte[] m_payload;
70
71         public int Type { get { return m_type; } }
72         public int Channel { get { return m_channel; } }
73         public byte[] Payload { get { return m_payload; } }
74
75         public MemoryStream m_accumulator;
76
77         public Frame() { }
78
79         public Frame(int type, int channel)
80         {
81             m_type = type;
82             m_channel = channel;
83             m_payload = null;
84             m_accumulator = new MemoryStream();
85         }
86
87         public Frame(int type, int channel, byte[] payload)
88         {
89             m_type = type;
90             m_channel = channel;
91             m_payload = payload;
92             m_accumulator = null;
93         }
94         
95         public static Frame ReadFrom(NetworkBinaryReader reader)
96         {
97             int type;
98             int channel;
99
100             type = reader.ReadByte();
101
102             if (type == 'A')
103             {
104                 // Probably an AMQP protocol header, otherwise meaningless
105                 ProcessProtocolHeader(reader);
106             }
107
108             channel = reader.ReadUInt16();
109             int payloadSize = reader.ReadInt32(); // FIXME - throw exn on unreasonable value
110             byte[] payload = reader.ReadBytes(payloadSize);
111             if (payload.Length != payloadSize)
112             {
113                 // Early EOF.
114                 throw new MalformedFrameException("Short frame - expected " +
115                                                   payloadSize + " bytes, got " +
116                                                   payload.Length + " bytes");
117             }
118
119             int frameEndMarker = reader.ReadByte();
120             if (frameEndMarker != CommonFraming.Constants.FrameEnd)
121             {
122                 throw new MalformedFrameException("Bad frame end marker: " + frameEndMarker);
123             }
124
125             return new Frame(type, channel, payload);
126         }
127
128         public static void ProcessProtocolHeader(NetworkBinaryReader reader)
129         {
130             try
131             {
132                 byte b1 = reader.ReadByte();
133                 byte b2 = reader.ReadByte();
134                 byte b3 = reader.ReadByte();
135                 if (b1 != 'M' || b2 != 'Q' || b3 != 'P')
136                 {
137                     throw new MalformedFrameException("Invalid AMQP protocol header from server");
138                 }
139
140                 int transportHigh = reader.ReadByte();
141                 int transportLow = reader.ReadByte();
142                 int serverMajor = reader.ReadByte();
143                 int serverMinor = reader.ReadByte();
144                 throw new PacketNotRecognizedException(transportHigh,
145                                                        transportLow,
146                                                        serverMajor,
147                                                        serverMinor);
148             }
149             catch (EndOfStreamException)
150             {
151                 // Ideally we'd wrap the EndOfStreamException in the
152                 // MalformedFrameException, but unfortunately the
153                 // design of MalformedFrameException's superclass,
154                 // ProtocolViolationException, doesn't permit
155                 // this. Fortunately, the call stack in the
156                 // EndOfStreamException is largely irrelevant at this
157                 // point, so can safely be ignored.
158                 throw new MalformedFrameException("Invalid AMQP protocol header from server");
159             }
160         }
161
162         public void FinishWriting()
163         {
164             if (m_accumulator != null)
165             {
166                 m_payload = m_accumulator.ToArray();
167                 m_accumulator = null;
168             }
169         }
170
171         public void WriteTo(NetworkBinaryWriter writer)
172         {
173             FinishWriting();
174             writer.Write((byte)m_type);
175             writer.Write((ushort)m_channel);
176             writer.Write((uint)m_payload.Length);
177             writer.Write((byte[])m_payload);
178             writer.Write((byte)CommonFraming.Constants.FrameEnd);
179         }
180
181         public NetworkBinaryReader GetReader()
182         {
183             return new NetworkBinaryReader(new MemoryStream(m_payload));
184         }
185
186         public NetworkBinaryWriter GetWriter()
187         {
188             return new NetworkBinaryWriter(m_accumulator);
189         }
190
191         public override string ToString()
192         {
193             return base.ToString() + string.Format("(type={0}, channel={1}, {2} bytes of payload)",
194                                                    Type,
195                                                    Channel,
196                                                    Payload == null
197                                                      ? "(null)"
198                                                      : Payload.Length.ToString());
199         }
200     }
201 }