1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3 * soup-session-feature.c: Miscellaneous session feature-provider interface
4 *
5 * Copyright (C) 2008 Red Hat, Inc.
6 */
7
8 #ifdef HAVE_CONFIG_H
9 #include <config.h>
10 #endif
11
12 #include "soup-session-feature.h"
13 #include "soup.h"
14 #include "soup-message-private.h"
15
16 /**
17 * SECTION:soup-session-feature
18 * @short_description: Interface for miscellaneous session features
19 *
20 * #SoupSessionFeature is the interface used by classes that extend
21 * the functionality of a #SoupSession. Some features like HTTP
22 * authentication handling are implemented internally via
23 * #SoupSessionFeature<!-- -->s. Other features can be added to the session
24 * by the application. (Eg, #SoupLogger, #SoupCookieJar.)
25 *
26 * See soup_session_add_feature(), etc, to add a feature to a session.
27 **/
28
29 /**
30 * SoupSessionFeature:
31 *
32 * An object that implement some sort of optional feature for
33 * #SoupSession.
34 *
35 * Since: 2.24
36 **/
37
38 /**
39 * SoupSessionFeatureInterface:
40 * @parent: The parent interface.
41 * @attach: Perform setup when a feature is added to a session
42 * @detach: Perform cleanup when a feature is removed from a session
43 * @request_queued: Proxies the session's #SoupSession::request_queued signal
44 * @request_started: Proxies the session's #SoupSession::request_started signal. Deprecated 2.50. Use #SoupMessage::starting instead.
45 * @request_unqueued: Proxies the session's #SoupSession::request_unqueued signal
46 * @add_feature: adds a sub-feature to the main feature
47 * @remove_feature: removes a sub-feature from the main feature
48 * @has_feature: tests if the feature includes a sub-feature
49 *
50 * The interface implemented by #SoupSessionFeature<!-- -->s.
51 *
52 * Since: 2.24
53 **/
54
G_DEFINE_INTERFACE(SoupSessionFeature,soup_session_feature,G_TYPE_OBJECT)55 G_DEFINE_INTERFACE (SoupSessionFeature, soup_session_feature, G_TYPE_OBJECT)
56
57 static void
58 weak_notify_unref (gpointer feature, GObject *ex_object)
59 {
60 g_object_unref (feature);
61 }
62
63 static void
request_queued(SoupSession * session,SoupMessage * msg,gpointer feature)64 request_queued (SoupSession *session, SoupMessage *msg, gpointer feature)
65 {
66 if (soup_message_disables_feature (msg, feature))
67 return;
68
69 g_object_ref (feature);
70 if (SOUP_SESSION_FEATURE_GET_CLASS (feature)->request_queued) {
71 SOUP_SESSION_FEATURE_GET_CLASS (feature)->
72 request_queued (feature, session, msg);
73 }
74 }
75
76 static void
request_started(SoupSession * session,SoupMessage * msg,SoupSocket * socket,gpointer feature)77 request_started (SoupSession *session, SoupMessage *msg,
78 SoupSocket *socket, gpointer feature)
79 {
80 if (soup_message_disables_feature (msg, feature))
81 return;
82
83 SOUP_SESSION_FEATURE_GET_CLASS (feature)->
84 request_started (feature, session, msg, socket);
85 }
86
87 static void
request_unqueued(SoupSession * session,SoupMessage * msg,gpointer feature)88 request_unqueued (SoupSession *session, SoupMessage *msg, gpointer feature)
89 {
90 if (soup_message_disables_feature (msg, feature))
91 return;
92
93 if (SOUP_SESSION_FEATURE_GET_CLASS (feature)->request_unqueued) {
94 SOUP_SESSION_FEATURE_GET_CLASS (feature)->
95 request_unqueued (feature, session, msg);
96 }
97 g_object_unref (feature);
98 }
99
100 static void
soup_session_feature_real_attach(SoupSessionFeature * feature,SoupSession * session)101 soup_session_feature_real_attach (SoupSessionFeature *feature, SoupSession *session)
102 {
103 g_object_weak_ref (G_OBJECT (session),
104 weak_notify_unref, g_object_ref (feature));
105
106 g_signal_connect (session, "request_queued",
107 G_CALLBACK (request_queued), feature);
108
109 if (SOUP_SESSION_FEATURE_GET_CLASS (feature)->request_started) {
110 g_signal_connect (session, "request_started",
111 G_CALLBACK (request_started), feature);
112 }
113
114 g_signal_connect (session, "request_unqueued",
115 G_CALLBACK (request_unqueued), feature);
116 }
117
118 void
soup_session_feature_attach(SoupSessionFeature * feature,SoupSession * session)119 soup_session_feature_attach (SoupSessionFeature *feature,
120 SoupSession *session)
121 {
122 g_return_if_fail (SOUP_IS_SESSION_FEATURE (feature));
123 g_return_if_fail (SOUP_IS_SESSION (session));
124
125 SOUP_SESSION_FEATURE_GET_CLASS (feature)->attach (feature, session);
126 }
127
128 static void
soup_session_feature_real_detach(SoupSessionFeature * feature,SoupSession * session)129 soup_session_feature_real_detach (SoupSessionFeature *feature, SoupSession *session)
130 {
131 g_object_weak_unref (G_OBJECT (session), weak_notify_unref, feature);
132
133 g_signal_handlers_disconnect_by_func (session, request_queued, feature);
134 g_signal_handlers_disconnect_by_func (session, request_started, feature);
135 g_signal_handlers_disconnect_by_func (session, request_unqueued, feature);
136
137 g_object_unref (feature);
138 }
139
140 void
soup_session_feature_detach(SoupSessionFeature * feature,SoupSession * session)141 soup_session_feature_detach (SoupSessionFeature *feature,
142 SoupSession *session)
143 {
144 g_return_if_fail (SOUP_IS_SESSION_FEATURE (feature));
145 g_return_if_fail (SOUP_IS_SESSION (session));
146
147 SOUP_SESSION_FEATURE_GET_CLASS (feature)->detach (feature, session);
148 }
149
150 static void
soup_session_feature_default_init(SoupSessionFeatureInterface * iface)151 soup_session_feature_default_init (SoupSessionFeatureInterface *iface)
152 {
153 iface->attach = soup_session_feature_real_attach;
154 iface->detach = soup_session_feature_real_detach;
155 }
156
157 /**
158 * soup_session_feature_add_feature:
159 * @feature: the "base" feature
160 * @type: the #GType of a "sub-feature"
161 *
162 * Adds a "sub-feature" of type @type to the base feature @feature.
163 * This is used for features that can be extended with multiple
164 * different types. Eg, the authentication manager can be extended
165 * with subtypes of #SoupAuth.
166 *
167 * Return value: %TRUE if @feature accepted @type as a subfeature.
168 *
169 * Since: 2.34
170 */
171 gboolean
soup_session_feature_add_feature(SoupSessionFeature * feature,GType type)172 soup_session_feature_add_feature (SoupSessionFeature *feature,
173 GType type)
174 {
175 SoupSessionFeatureInterface *feature_iface =
176 SOUP_SESSION_FEATURE_GET_CLASS (feature);
177
178 if (feature_iface->add_feature)
179 return feature_iface->add_feature (feature, type);
180 else
181 return FALSE;
182 }
183
184 /**
185 * soup_session_feature_remove_feature:
186 * @feature: the "base" feature
187 * @type: the #GType of a "sub-feature"
188 *
189 * Removes the "sub-feature" of type @type from the base feature
190 * @feature. See soup_session_feature_add_feature().
191 *
192 * Return value: %TRUE if @type was removed from @feature
193 *
194 * Since: 2.34
195 */
196 gboolean
soup_session_feature_remove_feature(SoupSessionFeature * feature,GType type)197 soup_session_feature_remove_feature (SoupSessionFeature *feature,
198 GType type)
199 {
200 SoupSessionFeatureInterface *feature_iface =
201 SOUP_SESSION_FEATURE_GET_CLASS (feature);
202
203 if (feature_iface->remove_feature)
204 return feature_iface->remove_feature (feature, type);
205 else
206 return FALSE;
207 }
208
209 /**
210 * soup_session_feature_has_feature:
211 * @feature: the "base" feature
212 * @type: the #GType of a "sub-feature"
213 *
214 * Tests if @feature has a "sub-feature" of type @type. See
215 * soup_session_feature_add_feature().
216 *
217 * Return value: %TRUE if @feature has a subfeature of type @type
218 *
219 * Since: 2.34
220 */
221 gboolean
soup_session_feature_has_feature(SoupSessionFeature * feature,GType type)222 soup_session_feature_has_feature (SoupSessionFeature *feature,
223 GType type)
224 {
225 SoupSessionFeatureInterface *feature_iface =
226 SOUP_SESSION_FEATURE_GET_CLASS (feature);
227
228 if (feature_iface->has_feature)
229 return feature_iface->has_feature (feature, type);
230 else
231 return FALSE;
232 }
233