1 // This source code is dual-licensed under the Apache License, version
2 // 2.0, and the Mozilla Public License, version 1.1.
6 //---------------------------------------------------------------------------
7 // Copyright (C) 2007, 2008 LShift Ltd., Cohesive Financial
8 // Technologies LLC., and Rabbit Technologies Ltd.
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
14 // http://www.apache.org/licenses/LICENSE-2.0
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 //---------------------------------------------------------------------------
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
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
36 // The Original Code is The RabbitMQ .NET Client.
38 // The Initial Developers of the Original Code are LShift Ltd.,
39 // Cohesive Financial Technologies LLC., and Rabbit Technologies Ltd.
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
46 // All Rights Reserved.
48 // Contributor(s): ______________________________________.
50 //---------------------------------------------------------------------------
54 using System.Net.Sockets;
55 using System.Collections;
57 using RabbitMQ.Client.Impl;
58 using RabbitMQ.Client.Exceptions;
60 namespace RabbitMQ.Client
62 ///<summary>Main entry point to the RabbitMQ .NET AMQP client
63 ///API. Constructs IConnection instances.</summary>
66 /// A simple example of connecting to a broker:
69 /// ConnectionFactory factory = new ConnectionFactory();
71 /// // The next three lines are optional:
72 /// factory.Parameters.UserName = ConnectionParameters.DefaultUser;
73 /// factory.Parameters.Password = ConnectionParameters.DefaultPass;
74 /// factory.Parameters.VirtualHost = ConnectionParameters.DefaultVHost;
76 /// IProtocol protocol = Protocols.DefaultProtocol;
77 /// IConnection conn = factory.CreateConnection(protocol, hostName, portNumber);
79 /// IModel ch = conn.CreateModel();
80 /// ushort ticket = ch.AccessRequest("/data");
82 /// // ... use ch's IModel methods ...
84 /// ch.Close(200, "Closing the channel");
85 /// conn.Close(200, "Closing the connection");
88 /// Please see also the API overview and tutorial in the User Guide.
91 /// Some of the static methods described below take, as a
92 /// convenience, a System.Uri instance representing an AMQP server
93 /// address. The use of Uri here is not standardised - Uri is
94 /// simply a convenient container for internet-address-like
95 /// components. In particular, the Uri "Scheme" property is
96 /// ignored: only the "Host" and "Port" properties are extracted.
99 public class ConnectionFactory
101 private ConnectionParameters m_parameters = new ConnectionParameters();
102 ///<summary>Retrieve the parameters this factory uses to
103 ///construct IConnection instances.</summary>
104 public ConnectionParameters Parameters
112 ///<summary>Constructs a ConnectionFactory with default values
113 ///for Parameters.</summary>
114 public ConnectionFactory()
118 protected virtual IConnection FollowRedirectChain
120 IDictionary connectionAttempts,
121 IDictionary connectionErrors,
122 ref AmqpTcpEndpoint[] mostRecentKnownHosts,
123 AmqpTcpEndpoint endpoint)
125 AmqpTcpEndpoint candidate = endpoint;
129 connectionAttempts.Contains(candidate)
130 ? (int) connectionAttempts[candidate]
132 connectionAttempts[candidate] = attemptCount + 1;
133 bool insist = attemptCount >= maxRedirects;
136 IProtocol p = candidate.Protocol;
137 IFrameHandler fh = p.CreateFrameHandler(candidate);
138 // At this point, we may be able to create
139 // and fully open a successful connection,
140 // in which case we're done, and the
141 // connection should be returned.
142 return p.CreateConnection(m_parameters, insist, fh);
143 } catch (RedirectException re) {
145 // We've been redirected, but we insisted that
146 // we shouldn't be redirected! Well-behaved
147 // brokers should never do this.
148 string message = string.Format("Server {0} ignored 'insist' flag, redirecting us to {1}",
151 throw new ProtocolViolationException(message);
153 // We've been redirected. Follow this new link
154 // in the chain, by setting
155 // mostRecentKnownHosts (in case the chain
156 // runs out), and updating candidate for the
157 // next time round the loop.
158 connectionErrors[candidate] = re;
159 mostRecentKnownHosts = re.KnownHosts;
164 } catch (Exception e) {
165 connectionErrors[candidate] = e;
170 protected virtual IConnection CreateConnection(int maxRedirects,
171 IDictionary connectionAttempts,
172 IDictionary connectionErrors,
173 params AmqpTcpEndpoint[] endpoints)
175 foreach (AmqpTcpEndpoint endpoint in endpoints)
177 AmqpTcpEndpoint[] mostRecentKnownHosts = new AmqpTcpEndpoint[0];
178 // ^^ holds a list of known-hosts that came back with
179 // a connection.redirect. If, once we reach the end of
180 // a chain of redirects, we still haven't managed to
181 // get a usable connection, we recurse on
182 // mostRecentKnownHosts, trying each of those in
183 // turn. Finally, if neither the initial
184 // chain-of-redirects for the current endpoint, nor
185 // the chains-of-redirects for each of the
186 // mostRecentKnownHosts gives us a usable connection,
187 // we give up on this particular endpoint, and
188 // continue with the foreach loop, trying the
189 // remainder of the array we were given.
190 IConnection conn = FollowRedirectChain(maxRedirects,
193 ref mostRecentKnownHosts,
199 // Connection to this endpoint failed at some point
200 // down the redirection chain - either the first
201 // entry, or one of the re.Host values from subsequent
202 // RedirectExceptions. We recurse into
203 // mostRecentKnownHosts, to see if one of those is
205 if (mostRecentKnownHosts.Length > 0) {
206 // Only bother recursing if we know of some
207 // hosts. If we were to recurse with no endpoints
208 // in the array, we'd stomp on
209 // mostRecentException, which makes debugging
210 // connectivity problems needlessly more
212 conn = CreateConnection(maxRedirects,
215 mostRecentKnownHosts);
224 ///<summary>Create a connection to the first available
225 ///endpoint in the list provided. Up to a maximum of
226 ///maxRedirects broker-originated redirects are permitted for
227 ///each endpoint tried.</summary>
228 public virtual IConnection CreateConnection(int maxRedirects,
229 params AmqpTcpEndpoint[] endpoints)
231 IDictionary connectionAttempts = new Hashtable();
232 IDictionary connectionErrors = new Hashtable();
233 IConnection conn = CreateConnection(maxRedirects,
240 throw new BrokerUnreachableException(connectionAttempts, connectionErrors);
243 ///<summary>Create a connection to the first available
244 ///endpoint in the list provided. No broker-originated
245 ///redirects are permitted.</summary>
246 public virtual IConnection CreateConnection(params AmqpTcpEndpoint[] endpoints)
248 return CreateConnection(0, endpoints);
251 ///<summary>Create a connection to the endpoint specified.</summary>
252 ///<exception cref="ArgumentException"/>
253 public IConnection CreateConnection(IProtocol version,
257 return CreateConnection(new AmqpTcpEndpoint[] {
258 new AmqpTcpEndpoint(version, hostName, portNumber)
262 ///<summary>Create a connection to the endpoint specified. The
263 ///port used is the default for the protocol.</summary>
264 ///<exception cref="ArgumentException"/>
265 public IConnection CreateConnection(IProtocol version, string hostName)
267 return CreateConnection(version, hostName, -1);
270 ///<summary>Create a connection to the endpoint specified.</summary>
272 /// Please see the class overview documentation for
273 /// information about the Uri format in use.
275 ///<exception cref="ArgumentException"/>
276 public IConnection CreateConnection(IProtocol version, Uri uri)
278 return CreateConnection(version, uri.Host, uri.Port);
281 ///<summary>Create a connection to the endpoint specified,
282 ///with the IProtocol from
283 ///Protocols.FromEnvironment().</summary>
285 /// Please see the class overview documentation for
286 /// information about the Uri format in use.
288 public IConnection CreateConnection(Uri uri)
290 return CreateConnection(Protocols.FromEnvironment(), uri.Host, uri.Port);
293 ///<summary>Create a connection to the host (and optional
294 ///port) specified, with the IProtocol from
295 ///Protocols.FromEnvironment(). The format of the address
296 ///string is the same as that accepted by
297 ///AmqpTcpEndpoint.Parse().</summary>
298 public IConnection CreateConnection(string address) {
299 return CreateConnection(AmqpTcpEndpoint.Parse(Protocols.FromEnvironment(), address));