2 * Copyright (C) 2009 Intel Corporation
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) version 3.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20 #include <syncevo/SoupTransportAgent.h>
21 #include <syncevo/SyncContext.h>
26 #include <libsoup/soup-status.h>
29 #ifdef HAVE_LIBSOUP_SOUP_GNOME_FEATURES_H
30 #include <libsoup/soup-gnome-features.h>
33 #include <syncevo/declarations.h>
36 SoupTransportAgent::SoupTransportAgent(GMainLoop *loop) :
37 m_session(soup_session_async_new()),
40 g_main_loop_new(NULL, TRUE),
46 #ifdef HAVE_LIBSOUP_SOUP_GNOME_FEATURES_H
47 // use default GNOME proxy settings
48 soup_session_add_feature_by_type(m_session.get(), SOUP_TYPE_PROXY_RESOLVER_GNOME);
52 SoupTransportAgent::~SoupTransportAgent()
54 if (m_session.get()) {
55 // ensure that no callbacks for this session will be triggered
56 // in the future, they would use a stale pointer to this agent
58 soup_session_abort(m_session.get());
62 void SoupTransportAgent::setURL(const std::string &url)
67 void SoupTransportAgent::setProxy(const std::string &proxy)
69 eptr<SoupURI, SoupURI, GLibUnref> uri(soup_uri_new(proxy.c_str()), "Proxy URI");
70 g_object_set(m_session.get(),
71 SOUP_SESSION_PROXY_URI, uri.get(),
75 void SoupTransportAgent::setProxyAuth(const std::string &user, const std::string &password)
78 * @TODO: handle "authenticate" signal for both proxy and HTTP server
79 * (https://sourceforge.net/tracker/index.php?func=detail&aid=2056162&group_id=146288&atid=764733).
81 * Proxy authentication is available, but still needs to be hooked up
82 * with libsoup. Should we make this interactive? Needs an additional
83 * API for TransportAgent into caller.
85 * HTTP authentication is not available.
88 m_proxyPassword = password;
91 void SoupTransportAgent::setSSL(const std::string &cacerts,
95 m_verifySSL = verifyServer || verifyHost;
99 void SoupTransportAgent::setContentType(const std::string &type)
101 m_contentType = type;
104 void SoupTransportAgent::setUserAgent(const std::string &agent)
106 g_object_set(m_session.get(),
107 SOUP_SESSION_USER_AGENT, agent.c_str(),
111 void SoupTransportAgent::setCallback (TransportCallback cb, void *data, int interval)
115 m_cbInterval = interval;
118 void SoupTransportAgent::send(const char *data, size_t len)
120 // ownership is transferred to libsoup in soup_session_queue_message()
121 eptr<SoupMessage, GObject> message(soup_message_new("POST", m_URL.c_str()));
122 if (!message.get()) {
123 SE_THROW_EXCEPTION(TransportException, "could not allocate SoupMessage");
126 // use CA certificates if available and needed,
127 // fail if not available and needed
129 if (!m_cacerts.empty()) {
130 g_object_set(m_session.get(), SOUP_SESSION_SSL_CA_FILE, m_cacerts.c_str(), NULL);
132 SoupURI *uri = soup_message_get_uri(message.get());
133 if (!strcmp(uri->scheme, SOUP_URI_SCHEME_HTTPS)) {
134 SE_THROW_EXCEPTION(TransportException, "SSL certificate checking requested, but no CA certificate file configured");
139 soup_message_set_request(message.get(), m_contentType.c_str(),
140 SOUP_MEMORY_TEMPORARY, data, len);
142 m_abortEventSource = g_timeout_add_seconds(ABORT_CHECK_INTERVAL, AbortCallback, static_cast<gpointer> (this));
144 m_message = message.get();
145 m_cbEventSource = g_timeout_add_seconds(m_cbInterval, TimeoutCallback, static_cast<gpointer> (this));
147 soup_session_queue_message(m_session.get(), message.release(),
148 SessionCallback, static_cast<gpointer>(this));
151 void SoupTransportAgent::cancel()
154 soup_session_abort(m_session.get());
155 if(g_main_loop_is_running(m_loop.get()))
156 g_main_loop_quit(m_loop.get());
159 TransportAgent::Status SoupTransportAgent::wait(bool noReply)
161 if (!m_failure.empty()) {
163 std::swap(failure, m_failure);
164 SE_THROW_EXCEPTION(TransportException, failure);
172 // block in main loop until our HandleSessionCallback() stops the loop
173 g_main_loop_run(m_loop.get());
179 /** For a canceled message, does not throw exception, just print a warning, the
180 * upper layer may decide to retry
182 if(m_status == TIME_OUT || m_status == FAILED){
184 std::swap(failure, m_failure);
185 SE_LOG_INFO(NULL, NULL, "SoupTransport Failure: %s", failure.c_str());
187 if (!m_failure.empty()) {
189 std::swap(failure, m_failure);
190 SE_THROW_EXCEPTION(TransportException, failure);
193 m_abortEventSource.set(0);
194 m_cbEventSource.set(0);
198 void SoupTransportAgent::getReply(const char *&data, size_t &len, std::string &contentType)
201 data = m_response->data;
202 len = m_response->length;
203 contentType = m_responseContentType;
210 void SoupTransportAgent::SessionCallback(SoupSession *session,
214 static_cast<SoupTransportAgent *>(user_data)->HandleSessionCallback(session, msg);
217 void SoupTransportAgent::HandleSessionCallback(SoupSession *session,
220 // keep a reference to the data
221 m_responseContentType = "";
222 if (msg->response_body) {
223 m_response = soup_message_body_flatten(msg->response_body);
224 const char *soupContentType = soup_message_headers_get(msg->response_headers,
226 if (soupContentType) {
227 m_responseContentType = soupContentType;
232 if (msg->status_code != 200) {
234 m_failure += " via libsoup: ";
235 m_failure += msg->reason_phrase ? msg->reason_phrase : "failed";
238 if (m_responseContentType.find("text") != std::string::npos) {
239 SE_LOG_DEBUG(NULL, NULL, "unexpected HTTP response: status %d/%s, content type %s, body:\n%.*s",
240 msg->status_code, msg->reason_phrase ? msg->reason_phrase : "<no reason>",
241 m_responseContentType.c_str(),
242 m_response ? (int)m_response->length : 0,
243 m_response ? m_response->data : "");
246 m_status = GOT_REPLY;
249 g_main_loop_quit(m_loop.get());
252 gboolean SoupTransportAgent::AbortCallback(gpointer transport)
254 const SuspendFlags &s_flags = SyncContext::getSuspendFlags();
256 if (s_flags.state == SuspendFlags::CLIENT_ABORT)
258 static_cast<SoupTransportAgent *>(transport)->cancel();
264 gboolean SoupTransportAgent::processCallback()
266 bool cont = m_cb(m_cbData);
268 //stop the message processing and mark status as timeout
269 guint message_status = SOUP_STATUS_CANCELLED;
270 soup_session_cancel_message(m_session.get(), m_message, message_status);
278 gboolean SoupTransportAgent::TimeoutCallback(gpointer transport)
280 SoupTransportAgent * sTransport = static_cast<SoupTransportAgent *>(transport);
281 return sTransport->processCallback();
286 #endif // ENABLE_LIBSOUP