Simplify glib/glib/tests setup
[glib.git] / gio / gsrvtarget.c
blob3422750fed0b077f81153fcc07d36225444a64af
1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
3 /* GIO - GLib Input, Output and Streaming Library
5 * Copyright (C) 2008 Red Hat, Inc.
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General
18 * Public License along with this library; if not, write to the
19 * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
20 * Boston, MA 02111-1307, USA.
23 #include "config.h"
24 #include <glib.h>
25 #include "glibintl.h"
27 #include "gsrvtarget.h"
29 #include <stdlib.h>
30 #include <string.h>
33 /**
34 * SECTION:gsrvtarget
35 * @short_description: DNS SRV record target
36 * @include: gio/gio.h
38 * SRV (service) records are used by some network protocols to provide
39 * service-specific aliasing and load-balancing. For example, XMPP
40 * (Jabber) uses SRV records to locate the XMPP server for a domain;
41 * rather than connecting directly to "example.com" or assuming a
42 * specific server hostname like "xmpp.example.com", an XMPP client
43 * would look up the "xmpp-client" SRV record for "example.com", and
44 * then connect to whatever host was pointed to by that record.
46 * You can use g_resolver_lookup_service() or
47 * g_resolver_lookup_service_async() to find the #GSrvTarget<!-- -->s
48 * for a given service. However, if you are simply planning to connect
49 * to the remote service, you can use #GNetworkService's
50 * #GSocketConnectable interface and not need to worry about
51 * #GSrvTarget at all.
54 struct _GSrvTarget {
55 gchar *hostname;
56 guint16 port;
58 guint16 priority;
59 guint16 weight;
62 /**
63 * GSrvTarget:
65 * A single target host/port that a network service is running on.
68 G_DEFINE_BOXED_TYPE (GSrvTarget, g_srv_target,
69 g_srv_target_copy, g_srv_target_free)
71 /**
72 * g_srv_target_new:
73 * @hostname: the host that the service is running on
74 * @port: the port that the service is running on
75 * @priority: the target's priority
76 * @weight: the target's weight
78 * Creates a new #GSrvTarget with the given parameters.
80 * You should not need to use this; normally #GSrvTarget<!-- -->s are
81 * created by #GResolver.
83 * Return value: a new #GSrvTarget.
85 * Since: 2.22
87 GSrvTarget *
88 g_srv_target_new (const gchar *hostname,
89 guint16 port,
90 guint16 priority,
91 guint16 weight)
93 GSrvTarget *target = g_slice_new0 (GSrvTarget);
95 target->hostname = g_strdup (hostname);
96 target->port = port;
97 target->priority = priority;
98 target->weight = weight;
100 return target;
104 * g_srv_target_copy:
105 * @target: a #GSrvTarget
107 * Copies @target
109 * Return value: a copy of @target
111 * Since: 2.22
113 GSrvTarget *
114 g_srv_target_copy (GSrvTarget *target)
116 return g_srv_target_new (target->hostname, target->port,
117 target->priority, target->weight);
121 * g_srv_target_free:
122 * @target: a #GSrvTarget
124 * Frees @target
126 * Since: 2.22
128 void
129 g_srv_target_free (GSrvTarget *target)
131 g_free (target->hostname);
132 g_slice_free (GSrvTarget, target);
136 * g_srv_target_get_hostname:
137 * @target: a #GSrvTarget
139 * Gets @target's hostname (in ASCII form; if you are going to present
140 * this to the user, you should use g_hostname_is_ascii_encoded() to
141 * check if it contains encoded Unicode segments, and use
142 * g_hostname_to_unicode() to convert it if it does.)
144 * Return value: @target's hostname
146 * Since: 2.22
148 const gchar *
149 g_srv_target_get_hostname (GSrvTarget *target)
151 return target->hostname;
155 * g_srv_target_get_port:
156 * @target: a #GSrvTarget
158 * Gets @target's port
160 * Return value: @target's port
162 * Since: 2.22
164 guint16
165 g_srv_target_get_port (GSrvTarget *target)
167 return target->port;
171 * g_srv_target_get_priority:
172 * @target: a #GSrvTarget
174 * Gets @target's priority. You should not need to look at this;
175 * #GResolver already sorts the targets according to the algorithm in
176 * RFC 2782.
178 * Return value: @target's priority
180 * Since: 2.22
182 guint16
183 g_srv_target_get_priority (GSrvTarget *target)
185 return target->priority;
189 * g_srv_target_get_weight:
190 * @target: a #GSrvTarget
192 * Gets @target's weight. You should not need to look at this;
193 * #GResolver already sorts the targets according to the algorithm in
194 * RFC 2782.
196 * Return value: @target's weight
198 * Since: 2.22
200 guint16
201 g_srv_target_get_weight (GSrvTarget *target)
203 return target->weight;
206 static gint
207 compare_target (gconstpointer a, gconstpointer b)
209 GSrvTarget *ta = (GSrvTarget *)a;
210 GSrvTarget *tb = (GSrvTarget *)b;
212 if (ta->priority == tb->priority)
214 /* Arrange targets of the same priority "in any order, except
215 * that all those with weight 0 are placed at the beginning of
216 * the list"
218 return ta->weight - tb->weight;
220 else
221 return ta->priority - tb->priority;
225 * g_srv_target_list_sort: (skip)
226 * @targets: a #GList of #GSrvTarget
228 * Sorts @targets in place according to the algorithm in RFC 2782.
230 * Return value: (transfer full): the head of the sorted list.
232 * Since: 2.22
234 GList *
235 g_srv_target_list_sort (GList *targets)
237 gint sum, num, val, priority, weight;
238 GList *t, *out, *tail;
239 GSrvTarget *target;
241 if (!targets)
242 return NULL;
244 if (!targets->next)
246 target = targets->data;
247 if (!strcmp (target->hostname, "."))
249 /* 'A Target of "." means that the service is decidedly not
250 * available at this domain.'
252 g_srv_target_free (target);
253 g_list_free (targets);
254 return NULL;
258 /* Sort input list by priority, and put the 0-weight targets first
259 * in each priority group. Initialize output list to %NULL.
261 targets = g_list_sort (targets, compare_target);
262 out = tail = NULL;
264 /* For each group of targets with the same priority, remove them
265 * from @targets and append them to @out in a valid order.
267 while (targets)
269 priority = ((GSrvTarget *)targets->data)->priority;
271 /* Count the number of targets at this priority level, and
272 * compute the sum of their weights.
274 sum = num = 0;
275 for (t = targets; t; t = t->next)
277 target = (GSrvTarget *)t->data;
278 if (target->priority != priority)
279 break;
280 sum += target->weight;
281 num++;
284 /* While there are still targets at this priority level... */
285 while (num)
287 /* Randomly select from the targets at this priority level,
288 * giving precedence to the ones with higher weight,
289 * according to the rules from RFC 2782.
291 val = g_random_int_range (0, sum + 1);
292 for (t = targets; ; t = t->next)
294 weight = ((GSrvTarget *)t->data)->weight;
295 if (weight >= val)
296 break;
297 val -= weight;
300 targets = g_list_remove_link (targets, t);
302 if (!out)
303 out = t;
304 else
305 tail->next = t;
306 tail = t;
308 sum -= weight;
309 num--;
313 return out;