Branch data Line data Source code
1 : : /* GIO - GLib Input, Output and Streaming Library
2 : : *
3 : : * Copyright (C) 2018 Igalia S.L.
4 : : *
5 : : * SPDX-License-Identifier: LGPL-2.1-or-later
6 : : *
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.1 of the License, or (at your option) any later version.
11 : : *
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.
16 : : *
17 : : * You should have received a copy of the GNU Lesser General
18 : : * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
19 : : */
20 : :
21 : : #include "mock-resolver.h"
22 : :
23 : : struct _MockResolver
24 : : {
25 : : GResolver parent_instance;
26 : : guint ipv4_delay_ms;
27 : : guint ipv6_delay_ms;
28 : : GList *ipv4_results;
29 : : GList *ipv6_results;
30 : : GError *ipv4_error;
31 : : GError *ipv6_error;
32 : : };
33 : :
34 : 52 : G_DEFINE_TYPE (MockResolver, mock_resolver, G_TYPE_RESOLVER)
35 : :
36 : : MockResolver *
37 : 17 : mock_resolver_new (void)
38 : : {
39 : 17 : return g_object_new (MOCK_TYPE_RESOLVER, NULL);
40 : : }
41 : :
42 : : void
43 : 6 : mock_resolver_set_ipv4_delay_ms (MockResolver *self, guint delay_ms)
44 : : {
45 : 6 : self->ipv4_delay_ms = delay_ms;
46 : 6 : }
47 : :
48 : : static gpointer
49 : 101 : copy_object (gconstpointer obj, gpointer user_data)
50 : : {
51 : 101 : return g_object_ref (G_OBJECT (obj));
52 : : }
53 : :
54 : : void
55 : 17 : mock_resolver_set_ipv4_results (MockResolver *self, GList *results)
56 : : {
57 : 17 : if (self->ipv4_results)
58 : 0 : g_list_free_full (self->ipv4_results, g_object_unref);
59 : 17 : self->ipv4_results = g_list_copy_deep (results, copy_object, NULL);
60 : 17 : }
61 : :
62 : : void
63 : 6 : mock_resolver_set_ipv4_error (MockResolver *self, GError *error)
64 : : {
65 : 6 : g_clear_error (&self->ipv4_error);
66 : 6 : if (error)
67 : 6 : self->ipv4_error = g_error_copy (error);
68 : 6 : }
69 : :
70 : : void
71 : 6 : mock_resolver_set_ipv6_delay_ms (MockResolver *self, guint delay_ms)
72 : : {
73 : 6 : self->ipv6_delay_ms = delay_ms;
74 : 6 : }
75 : :
76 : : void
77 : 15 : mock_resolver_set_ipv6_results (MockResolver *self, GList *results)
78 : : {
79 : 15 : if (self->ipv6_results)
80 : 0 : g_list_free_full (self->ipv6_results, g_object_unref);
81 : 15 : self->ipv6_results = g_list_copy_deep (results, copy_object, NULL);
82 : 15 : }
83 : :
84 : : void
85 : 7 : mock_resolver_set_ipv6_error (MockResolver *self, GError *error)
86 : : {
87 : 7 : g_clear_error (&self->ipv6_error);
88 : 7 : if (error)
89 : 7 : self->ipv6_error = g_error_copy (error);
90 : 7 : }
91 : :
92 : : static gboolean lookup_by_name_cb (gpointer user_data);
93 : :
94 : : /* Core of the implementation of `lookup_by_name()` in the mock resolver.
95 : : *
96 : : * It creates a #GSource which will become ready with the resolver results. It
97 : : * will become ready either after a timeout, or as an idle callback. This
98 : : * simulates doing some actual network-based resolution work.
99 : : *
100 : : * A previous implementation of this did the work in a thread, but that made it
101 : : * hard to synchronise the timeouts with the #GResolver failure timeouts in the
102 : : * calling thread, as spawning a worker thread could be subject to non-trivial
103 : : * delays. */
104 : : static void
105 : 33 : do_lookup_by_name (MockResolver *self,
106 : : GTask *task,
107 : : GResolverNameLookupFlags flags)
108 : : {
109 : 33 : GSource *source = NULL;
110 : :
111 : 33 : g_task_set_task_data (task, GINT_TO_POINTER (flags), NULL);
112 : :
113 : 33 : if (flags == G_RESOLVER_NAME_LOOKUP_FLAGS_IPV4_ONLY)
114 : 16 : source = g_timeout_source_new (self->ipv4_delay_ms);
115 : 17 : else if (flags == G_RESOLVER_NAME_LOOKUP_FLAGS_IPV6_ONLY)
116 : 16 : source = g_timeout_source_new (self->ipv6_delay_ms);
117 : 1 : else if (flags == G_RESOLVER_NAME_LOOKUP_FLAGS_DEFAULT)
118 : 1 : source = g_idle_source_new ();
119 : : else
120 : : g_assert_not_reached ();
121 : :
122 : 33 : g_source_set_callback (source, lookup_by_name_cb, g_object_ref (task), g_object_unref);
123 : 33 : g_source_attach (source, g_main_context_get_thread_default ());
124 : 33 : g_source_unref (source);
125 : 33 : }
126 : :
127 : : static gboolean
128 : 32 : lookup_by_name_cb (gpointer user_data)
129 : : {
130 : 32 : GTask *task = G_TASK (user_data);
131 : 32 : MockResolver *self = g_task_get_source_object (task);
132 : 32 : GResolverNameLookupFlags flags = GPOINTER_TO_INT (g_task_get_task_data (task));
133 : :
134 : 32 : if (flags == G_RESOLVER_NAME_LOOKUP_FLAGS_IPV4_ONLY)
135 : : {
136 : 16 : if (self->ipv4_error)
137 : 6 : g_task_return_error (task, g_error_copy (self->ipv4_error));
138 : : else
139 : 10 : g_task_return_pointer (task, g_list_copy_deep (self->ipv4_results, copy_object, NULL), NULL);
140 : : }
141 : 16 : else if (flags == G_RESOLVER_NAME_LOOKUP_FLAGS_IPV6_ONLY)
142 : : {
143 : 15 : if (self->ipv6_error)
144 : 6 : g_task_return_error (task, g_error_copy (self->ipv6_error));
145 : : else
146 : 9 : g_task_return_pointer (task, g_list_copy_deep (self->ipv6_results, copy_object, NULL), NULL);
147 : : }
148 : 1 : else if (flags == G_RESOLVER_NAME_LOOKUP_FLAGS_DEFAULT)
149 : : {
150 : : /* This is only the minimal implementation needed for some tests */
151 : 1 : g_assert (self->ipv4_error == NULL && self->ipv6_error == NULL && self->ipv6_results == NULL);
152 : 1 : g_task_return_pointer (task, g_list_copy_deep (self->ipv4_results, copy_object, NULL), NULL);
153 : : }
154 : : else
155 : : g_assert_not_reached ();
156 : :
157 : 32 : return G_SOURCE_REMOVE;
158 : : }
159 : :
160 : : static void
161 : 32 : lookup_by_name_with_flags_async (GResolver *resolver,
162 : : const gchar *hostname,
163 : : GResolverNameLookupFlags flags,
164 : : GCancellable *cancellable,
165 : : GAsyncReadyCallback callback,
166 : : gpointer user_data)
167 : : {
168 : 32 : MockResolver *self = MOCK_RESOLVER (resolver);
169 : 32 : GTask *task = NULL;
170 : :
171 : 32 : task = g_task_new (resolver, cancellable, callback, user_data);
172 : 32 : g_task_set_source_tag (task, lookup_by_name_with_flags_async);
173 : :
174 : 32 : do_lookup_by_name (self, task, flags);
175 : :
176 : 32 : g_object_unref (task);
177 : 32 : }
178 : :
179 : : static void
180 : 1 : async_result_cb (GObject *source_object,
181 : : GAsyncResult *result,
182 : : gpointer user_data)
183 : : {
184 : 1 : GAsyncResult **result_out = user_data;
185 : :
186 : 1 : g_assert (*result_out == NULL);
187 : 1 : *result_out = g_object_ref (result);
188 : :
189 : 1 : g_main_context_wakeup (g_main_context_get_thread_default ());
190 : 1 : }
191 : :
192 : : static GList *
193 : 1 : lookup_by_name (GResolver *resolver,
194 : : const gchar *hostname,
195 : : GCancellable *cancellable,
196 : : GError **error)
197 : : {
198 : 1 : MockResolver *self = MOCK_RESOLVER (resolver);
199 : 1 : GMainContext *context = NULL;
200 : 1 : GList *result = NULL;
201 : 1 : GAsyncResult *async_result = NULL;
202 : 1 : GTask *task = NULL;
203 : :
204 : 1 : context = g_main_context_new ();
205 : 1 : g_main_context_push_thread_default (context);
206 : :
207 : 1 : task = g_task_new (resolver, cancellable, async_result_cb, &async_result);
208 : 1 : g_task_set_source_tag (task, lookup_by_name);
209 : :
210 : : /* Set up the resolution job. */
211 : 1 : do_lookup_by_name (self, task, G_RESOLVER_NAME_LOOKUP_FLAGS_DEFAULT);
212 : :
213 : : /* Wait for it to complete synchronously. */
214 : 2 : while (async_result == NULL)
215 : 1 : g_main_context_iteration (context, TRUE);
216 : :
217 : 1 : result = g_task_propagate_pointer (G_TASK (async_result), error);
218 : 1 : g_object_unref (async_result);
219 : :
220 : 1 : g_assert_finalize_object (task);
221 : :
222 : 1 : g_main_context_pop_thread_default (context);
223 : 1 : g_main_context_unref (context);
224 : :
225 : 1 : return g_steal_pointer (&result);
226 : : }
227 : :
228 : : static GList *
229 : 31 : lookup_by_name_with_flags_finish (GResolver *resolver,
230 : : GAsyncResult *result,
231 : : GError **error)
232 : : {
233 : 31 : return g_task_propagate_pointer (G_TASK (result), error);
234 : : }
235 : :
236 : : static void
237 : 16 : mock_resolver_finalize (GObject *object)
238 : : {
239 : 16 : MockResolver *self = (MockResolver*)object;
240 : :
241 : 16 : g_clear_error (&self->ipv4_error);
242 : 16 : g_clear_error (&self->ipv6_error);
243 : 16 : if (self->ipv6_results)
244 : 14 : g_list_free_full (self->ipv6_results, g_object_unref);
245 : 16 : if (self->ipv4_results)
246 : 16 : g_list_free_full (self->ipv4_results, g_object_unref);
247 : :
248 : 16 : G_OBJECT_CLASS (mock_resolver_parent_class)->finalize (object);
249 : 16 : }
250 : :
251 : : static void
252 : 1 : mock_resolver_class_init (MockResolverClass *klass)
253 : : {
254 : 1 : GResolverClass *resolver_class = G_RESOLVER_CLASS (klass);
255 : 1 : GObjectClass *object_class = G_OBJECT_CLASS (klass);
256 : 1 : resolver_class->lookup_by_name_with_flags_async = lookup_by_name_with_flags_async;
257 : 1 : resolver_class->lookup_by_name_with_flags_finish = lookup_by_name_with_flags_finish;
258 : 1 : resolver_class->lookup_by_name = lookup_by_name;
259 : 1 : object_class->finalize = mock_resolver_finalize;
260 : 1 : }
261 : :
262 : : static void
263 : 17 : mock_resolver_init (MockResolver *self)
264 : : {
265 : 17 : }
|