#include "soup-queue.h"
#include "soup-transfer.h"
+typedef struct {
+ SoupHandlerEvent type;
+ gchar *name;
+ SoupHandlerWhen order;
+ SoupHandlerFilter filter;
+ SoupHandlerFn handler_cb;
+ gpointer user_data;
+
+ SoupMessage *msg;
+ guint timeout_tag;
+} SoupHandlerData;
+
/**
* soup_message_new:
* @context: a %SoupContext for the destination endpoint.
}
static void
+handler_free (SoupHandlerData *data)
+{
+ if (data->filter.type == SOUP_FILTER_HEADER)
+ g_free ((gchar *) data->filter.data.header);
+
+ if (data->timeout_tag)
+ g_source_remove (data->timeout_tag);
+
+ data->msg->priv->content_handlers =
+ g_slist_remove (data->msg->priv->content_handlers, data);
+
+ g_free (data->name);
+ g_free (data);
+}
+
+static void
finalize_message (SoupMessage *req)
{
soup_context_unref (req->context);
soup_message_clear_headers (req->response_headers);
g_hash_table_destroy (req->response_headers);
- g_slist_foreach (req->priv->content_handlers, (GFunc) g_free, NULL);
- g_slist_free (req->priv->content_handlers);
+ while (req->priv->content_handlers) {
+ SoupHandlerData *data = req->priv->content_handlers->data;
+ handler_free (data);
+ }
g_free ((gchar *) req->errorphrase);
g_free (req->priv);
}
/**
- * soup_message_set_request_header:
- * @req: a %SoupMessage.
- * @name: header name.
- * @value: header value.
- *
- * ** DEPRECATED **
- *
- * Adds a new transport header to be sent on an outgoing request. Passing a NULL
- * @value will remove all headers with a name equal to @name.
- */
-void
-soup_message_set_request_header (SoupMessage *req,
- const gchar *name,
- const gchar *value)
-{
- g_return_if_fail (req != NULL);
- g_return_if_fail (name != NULL || name [0] != '\0');
-
- g_warning ("soup_message_set_request_header is DEPRECATED. Use "
- "soup_message_add_header, with msg->request_headers as "
- "the first argument.\n");
-
- soup_message_add_header (req->request_headers, name, value);
-}
-
-/**
- * soup_message_get_request_header:
- * @req: a %SoupMessage.
- * @name: header name.
- *
- * ** DEPRECATED **
- *
- * Lookup the first transport request header with a key equal to @name.
- *
- * Return value: the header's value or NULL if not found.
- */
-const gchar *
-soup_message_get_request_header (SoupMessage *req,
- const gchar *name)
-{
- GSList *vals;
- g_return_val_if_fail (req != NULL, NULL);
- g_return_val_if_fail (name != NULL || name [0] != '\0', NULL);
-
- g_warning ("soup_message_get_request_header is DEPRECATED. Use "
- "soup_message_get_header, with msg->request_headers as "
- "the first argument.\n");
-
- if (req->request_headers) {
- vals = g_hash_table_lookup (req->request_headers, name);
- if (vals)
- return vals->data;
- }
-
- return NULL;
-}
-
-/**
- * soup_message_set_response_header:
- * @req: a %SoupMessage.
- * @name: header name.
- * @value: header value.
- *
- * ** DEPRECATED **
- *
- * Adds a new transport header to be sent on an outgoing response. Passing a
- * NULL @value will remove all headers with a name equal to @name.
- */
-void
-soup_message_set_response_header (SoupMessage *req,
- const gchar *name,
- const gchar *value)
-{
- g_return_if_fail (req != NULL);
- g_return_if_fail (name != NULL || name [0] != '\0');
-
- g_warning ("soup_message_set_response_header is DEPRECATED. Use "
- "soup_message_add_header, with msg->response_headers as "
- "the first argument.\n");
-
- soup_message_add_header (req->response_headers, name, value);
-}
-
-/**
- * soup_message_get_response_header:
- * @req: a %SoupMessage.
- * @name: header name.
- *
- * ** DEPRECATED **
- *
- * Lookup the transport response header with a key equal to @name.
- *
- * Return value: the header's value or NULL if not found.
- */
-const gchar *
-soup_message_get_response_header (SoupMessage *req,
- const gchar *name)
-{
- GSList *vals;
- g_return_val_if_fail (req != NULL, NULL);
- g_return_val_if_fail (name != NULL || name [0] != '\0', NULL);
-
- g_warning ("soup_message_get_response_header is DEPRECATED. Use "
- "soup_message_get_header, with msg->response_headers as "
- "the first argument.\n");
-
- if (req->response_headers) {
- vals = g_hash_table_lookup (req->response_headers, name);
- if (vals)
- return vals->data;
- }
-
- return NULL;
-}
-
-/**
* soup_message_queue:
* @req: a %SoupMessage.
* @callback: a %SoupCallbackFn which will be called after the message completes
return msg->errorclass;
}
-static void
-authorize_handler (SoupMessage *msg, gboolean proxy)
+static SoupHandlerResult
+authorize_handler (SoupMessage *msg, gpointer user_data)
{
+ gboolean proxy = GPOINTER_TO_INT (user_data);
const GSList *vals;
SoupAuth *auth, *old_auth;
SoupContext *ctx;
"Unknown authentication scheme required by "
"proxy" :
"Unknown authentication scheme required");
- return;
+ return SOUP_HANDLER_RESTART;
}
/*
soup_auth_set_context (auth, ctx);
- soup_message_queue (msg, msg->priv->callback, msg->priv->user_data);
-
- return;
+ return SOUP_HANDLER_RESEND;
THROW_CANT_AUTHENTICATE:
soup_message_set_error (msg,
proxy ?
SOUP_ERROR_CANT_AUTHENTICATE_PROXY :
SOUP_ERROR_CANT_AUTHENTICATE);
+ return SOUP_HANDLER_RESTART;
}
-static void
+static SoupHandlerResult
redirect_handler (SoupMessage *msg, gpointer user_data)
{
const gchar *new_loc;
if (msg->errorclass != SOUP_ERROR_CLASS_REDIRECT ||
- msg->priv->msg_flags & SOUP_MESSAGE_NO_REDIRECT) return;
+ msg->priv->msg_flags & SOUP_MESSAGE_NO_REDIRECT)
+ return SOUP_HANDLER_CONTINUE;
new_loc = soup_message_get_header (msg->response_headers, "Location");
soup_message_set_context (msg, new_ctx);
soup_context_unref (new_ctx);
- soup_message_queue (msg,
- msg->priv->callback,
- msg->priv->user_data);
+ return SOUP_HANDLER_RESEND;
}
- return;
+ return SOUP_HANDLER_CONTINUE;
INVALID_REDIRECT:
soup_message_set_error_full (msg,
SOUP_ERROR_MALFORMED,
"Invalid Redirect URL");
+ return SOUP_HANDLER_RESTART;
}
-typedef enum {
- RESPONSE_HEADER_HANDLER = 1,
- RESPONSE_ERROR_CODE_HANDLER,
- RESPONSE_ERROR_CLASS_HANDLER
-} SoupHandlerKind;
-
-typedef struct {
- SoupHandlerType type;
- SoupCallbackFn handler_cb;
- gpointer user_data;
-
- SoupHandlerKind kind;
- union {
- guint errorcode;
- SoupErrorClass errorclass;
- const gchar *header;
- } data;
-} SoupHandlerData;
-
static SoupHandlerData global_handlers [] = {
/*
* Handle redirect response codes 300, 301, 302, 303, and 305.
*/
{
- SOUP_HANDLER_PRE_BODY,
+ SOUP_HANDLER_HEADERS,
+ "redirect",
+ 0,
+ {
+ SOUP_FILTER_HEADER,
+ {
+ (guint) "Location"
+ },
+ },
redirect_handler,
NULL,
- RESPONSE_HEADER_HANDLER,
- { (guint) "Location" }
},
/*
* Handle authorization.
*/
{
- SOUP_HANDLER_PRE_BODY,
- (SoupCallbackFn) authorize_handler,
+ SOUP_HANDLER_HEADERS,
+ "authenticate",
+ 0,
+ {
+ SOUP_FILTER_ERROR_CODE,
+ {
+ 401
+ },
+ },
+ authorize_handler,
GINT_TO_POINTER (FALSE),
- RESPONSE_ERROR_CODE_HANDLER,
- { 401 }
},
/*
* Handle proxy authorization.
*/
{
- SOUP_HANDLER_PRE_BODY,
- (SoupCallbackFn) authorize_handler,
+ SOUP_HANDLER_HEADERS,
+ "proxy-authenticate",
+ 0,
+ {
+ SOUP_FILTER_ERROR_CODE,
+ {
+ 407
+ },
+ },
+ authorize_handler,
GINT_TO_POINTER (TRUE),
- RESPONSE_ERROR_CODE_HANDLER,
- { 407 }
},
{ 0 }
};
-static inline void
-run_handler (SoupMessage *msg,
- SoupHandlerType invoke_type,
- SoupHandlerData *data)
+static inline SoupHandlerResult
+run_handler (SoupMessage *msg,
+ SoupHandlerEvent invoke_type,
+ SoupHandlerEvent when,
+ SoupHandlerData *data)
{
- if (data->type != invoke_type) return;
+ SoupHandlerResult result;
- switch (data->kind) {
- case RESPONSE_HEADER_HANDLER:
+ if (data->type != invoke_type || data->order != when)
+ return SOUP_HANDLER_CONTINUE;
+
+ switch (data->filter.type) {
+ case SOUP_FILTER_HEADER:
if (!soup_message_get_header (msg->response_headers,
- data->data.header))
- return;
+ data->filter.data.header))
+ return SOUP_HANDLER_CONTINUE;
break;
- case RESPONSE_ERROR_CODE_HANDLER:
- if (msg->errorcode != data->data.errorcode) return;
+ case SOUP_FILTER_ERROR_CODE:
+ if (msg->errorcode != data->filter.data.errorcode)
+ return SOUP_HANDLER_CONTINUE;
break;
- case RESPONSE_ERROR_CLASS_HANDLER:
- if (msg->errorclass != data->data.errorclass) return;
+ case SOUP_FILTER_ERROR_CLASS:
+ if (msg->errorclass != data->filter.data.errorclass)
+ return SOUP_HANDLER_CONTINUE;
break;
+ case SOUP_FILTER_TIMEOUT:
+ return SOUP_HANDLER_CONTINUE;
default:
break;
}
- (*data->handler_cb) (msg, data->user_data);
+ result = (*data->handler_cb) (msg, data->user_data);
+
+ switch (result) {
+ case SOUP_HANDLER_STOP:
+ if (invoke_type == SOUP_HANDLER_FINISHED &&
+ msg->errorclass != SOUP_ERROR_CLASS_INFORMATIONAL)
+ soup_message_issue_callback (msg);
+ break;
+ case SOUP_HANDLER_KILL:
+ soup_message_issue_callback (msg);
+ break;
+ case SOUP_HANDLER_RESEND:
+ if (msg->status != SOUP_STATUS_QUEUED)
+ soup_message_queue (msg,
+ msg->priv->callback,
+ msg->priv->user_data);
+ break;
+ default:
+ if (msg->status == SOUP_STATUS_QUEUED)
+ result = SOUP_HANDLER_RESEND;
+ break;
+ }
+
+ return result;
}
+#define PROCESS_HANDLER_RESULT(result) ({ \
+ switch (result) { \
+ case SOUP_HANDLER_STOP: \
+ return FALSE; \
+ case SOUP_HANDLER_KILL: \
+ case SOUP_HANDLER_RESEND: \
+ return TRUE; \
+ case SOUP_HANDLER_RESTART: \
+ goto RESTART; \
+ default: \
+ break; \
+ } \
+})
+
/*
* Run each handler with matching criteria (first per-message then global
* handlers). If a handler requeues a message, we stop processing and terminate
* processing.
*/
gboolean
-soup_message_run_handlers (SoupMessage *msg, SoupHandlerType invoke_type)
+soup_message_run_handlers (SoupMessage *msg, SoupHandlerEvent invoke_type)
{
GSList *list;
SoupHandlerData *data;
+ SoupHandlerResult result;
g_return_val_if_fail (msg != NULL, FALSE);
+ RESTART:
+ /*
+ * Pre-Global handlers
+ */
for (list = msg->priv->content_handlers; list; list = list->next) {
data = list->data;
-
- run_handler (msg, invoke_type, data);
-
- if (msg->status == SOUP_STATUS_QUEUED) return TRUE;
+ result = run_handler (msg,
+ invoke_type,
+ SOUP_HANDLER_FIRST,
+ data);
+ PROCESS_HANDLER_RESULT (result);
}
+ /*
+ * Global handlers
+ */
for (data = global_handlers; data->type; data++) {
- run_handler (msg, invoke_type, data);
+ result = run_handler (msg,
+ invoke_type,
+ 0,
+ data);
+ PROCESS_HANDLER_RESULT (result);
+ }
- if (msg->status == SOUP_STATUS_QUEUED) return TRUE;
+ /*
+ * Post-Global handlers
+ */
+ for (list = msg->priv->content_handlers; list; list = list->next) {
+ data = list->data;
+ result = run_handler (msg,
+ invoke_type,
+ SOUP_HANDLER_LAST,
+ data);
+ PROCESS_HANDLER_RESULT (result);
}
/*
- * Issue final callback if the invoke_type is POST_BODY and the error
+ * Issue final callback if the invoke_type is FINISHED and the error
* class is not INFORMATIONAL.
*/
- if (invoke_type == SOUP_HANDLER_POST_BODY &&
+ if (invoke_type == SOUP_HANDLER_FINISHED &&
msg->errorclass != SOUP_ERROR_CLASS_INFORMATIONAL) {
soup_message_issue_callback (msg);
return TRUE;
return FALSE;
}
-static void
-add_handler (SoupMessage *msg,
- SoupHandlerType type,
- SoupCallbackFn handler_cb,
- gpointer user_data,
- SoupHandlerKind kind,
- const gchar *header,
- guint errorcode,
- guint errorclass)
+static gboolean
+timeout_handler (gpointer user_data)
{
- SoupHandlerData *data;
+ SoupHandlerData *data = user_data;
+ SoupMessage *msg = data->msg;
+ SoupHandlerResult result;
+ GSList *iter;
+
+ switch (data->type) {
+ case SOUP_HANDLER_PREPARE:
+ if (msg->status >= SOUP_STATUS_SENDING_REQUEST)
+ goto REMOVE_SOURCE;
+ case SOUP_HANDLER_HEADERS:
+ case SOUP_HANDLER_DATA:
+ if (msg->status >= SOUP_STATUS_READING_RESPONSE &&
+ g_hash_table_size (msg->response_headers) > 0)
+ goto REMOVE_SOURCE;
+ case SOUP_HANDLER_FINISHED:
+ if (msg->status == SOUP_STATUS_FINISHED)
+ goto REMOVE_SOURCE;
+ case SOUP_HANDLER_DATA_SENT:
+ iter = msg->priv->content_handlers;
+ while (iter) {
+ SoupHandlerData *hd = iter->data;
+ if (!g_strcasecmp (hd->name, "server-message"))
+ goto REMOVE_SOURCE;
+ }
+ }
- data = g_new0 (SoupHandlerData, 1);
- data->type = type;
- data->handler_cb = handler_cb;
- data->user_data = user_data;
- data->kind = kind;
+ result = (*data->handler_cb) (msg, data->user_data);
- switch (kind) {
- case RESPONSE_HEADER_HANDLER:
- data->data.header = header;
+ switch (result) {
+ case SOUP_HANDLER_KILL:
+ soup_message_cancel (msg);
break;
- case RESPONSE_ERROR_CODE_HANDLER:
- data->data.errorcode = errorcode;
- break;
- case RESPONSE_ERROR_CLASS_HANDLER:
- data->data.errorclass = errorclass;
+ case SOUP_HANDLER_RESEND:
+ soup_message_queue (msg,
+ msg->priv->callback,
+ msg->priv->user_data);
break;
default:
break;
}
- msg->priv->content_handlers =
- g_slist_append (msg->priv->content_handlers, data);
+ REMOVE_SOURCE:
+ data->timeout_tag = 0;
+ return FALSE;
}
void
-soup_message_add_header_handler (SoupMessage *msg,
- const gchar *header,
- SoupHandlerType type,
- SoupCallbackFn handler_cb,
- gpointer user_data)
+soup_message_add_handler_full (SoupMessage *msg,
+ const gchar *name,
+ SoupHandlerEvent type,
+ SoupHandlerWhen order,
+ SoupHandlerFilter *filter,
+ SoupHandlerFn handler_cb,
+ gpointer user_data)
{
+ SoupHandlerData *data;
+
g_return_if_fail (msg != NULL);
- g_return_if_fail (header != NULL);
+ g_return_if_fail (type != 0);
+ g_return_if_fail (order != 0);
g_return_if_fail (handler_cb != NULL);
- add_handler (msg,
- type,
- handler_cb,
- user_data,
- RESPONSE_HEADER_HANDLER,
- header,
- 0,
- 0);
+ data = g_new0 (SoupHandlerData, 1);
+ data->type = type;
+ data->handler_cb = handler_cb;
+ data->user_data = user_data;
+ data->name = g_strdup (name);
+ data->order = order;
+ data->msg = msg;
+
+ if (filter) {
+ data->filter.type = filter->type;
+
+ switch (filter->type) {
+ case SOUP_FILTER_HEADER:
+ data->filter.data.header =
+ g_strdup (filter->data.header);
+ break;
+ case SOUP_FILTER_ERROR_CODE:
+ data->filter.data.errorcode = filter->data.errorcode;
+ break;
+ case SOUP_FILTER_ERROR_CLASS:
+ data->filter.data.errorclass = filter->data.errorclass;
+ break;
+ case SOUP_FILTER_TIMEOUT:
+ data->filter.data.timeout = filter->data.timeout;
+ data->timeout_tag =
+ g_timeout_add (filter->data.timeout * 1000,
+ timeout_handler,
+ data);
+ break;
+ default:
+ break;
+ }
+ }
+
+ msg->priv->content_handlers =
+ g_slist_append (msg->priv->content_handlers, data);
}
void
-soup_message_add_error_code_handler (SoupMessage *msg,
- guint errorcode,
- SoupHandlerType type,
- SoupCallbackFn handler_cb,
- gpointer user_data)
+soup_message_add_handler (SoupMessage *msg,
+ SoupHandlerEvent type,
+ SoupHandlerFilter *filter,
+ SoupHandlerFn handler_cb,
+ gpointer user_data)
{
g_return_if_fail (msg != NULL);
- g_return_if_fail (errorcode != 0);
+ g_return_if_fail (type != 0);
g_return_if_fail (handler_cb != NULL);
- add_handler (msg,
- type,
- handler_cb,
- user_data,
- RESPONSE_ERROR_CODE_HANDLER,
- NULL,
- errorcode,
- 0);
+ soup_message_add_handler_full (msg,
+ NULL,
+ type,
+ SOUP_HANDLER_LAST,
+ filter,
+ handler_cb,
+ user_data);
+}
+
+GSList *
+soup_message_list_handlers (SoupMessage *msg)
+{
+ GSList *ret = NULL, *list;
+
+ g_return_val_if_fail (msg != NULL, NULL);
+
+ for (list = msg->priv->content_handlers; list; list = list->next) {
+ SoupHandlerData *data = list->data;
+ if (data->name)
+ ret = g_slist_append (ret, data->name);
+ }
+
+ return ret;
}
void
-soup_message_add_error_class_handler (SoupMessage *msg,
- SoupErrorClass errorclass,
- SoupHandlerType type,
- SoupCallbackFn handler_cb,
- gpointer user_data)
+soup_message_remove_handler (SoupMessage *msg,
+ gchar *name)
{
+ GSList *iter;
+
g_return_if_fail (msg != NULL);
- g_return_if_fail (errorclass != 0);
- g_return_if_fail (handler_cb != NULL);
+ g_return_if_fail (name != NULL);
+
+ iter = msg->priv->content_handlers;
+ while (iter) {
+ SoupHandlerData *data = iter->data;
+
+ if (data->name && !g_strcasecmp (data->name, name)) {
+ handler_free (data);
+ break;
+ }
- add_handler (msg,
- type,
- handler_cb,
- user_data,
- RESPONSE_ERROR_CLASS_HANDLER,
- NULL,
- 0,
- errorclass);
+ iter = iter->next;
+ }
}
void
-soup_message_add_handler (SoupMessage *msg,
- SoupHandlerType type,
- SoupCallbackFn handler_cb,
- gpointer user_data)
+soup_message_remove_handler_by_func (SoupMessage *msg,
+ SoupHandlerFn handler_cb)
{
+ GSList *iter;
+
g_return_if_fail (msg != NULL);
g_return_if_fail (handler_cb != NULL);
- add_handler (msg,
- type,
- handler_cb,
- user_data,
- 0,
- NULL,
- 0,
- 0);
+ iter = msg->priv->content_handlers;
+ while (iter) {
+ SoupHandlerData *data = iter->data;
+
+ if (data->handler_cb == handler_cb) {
+ handler_free (data);
+ break;
+ }
+
+ iter = iter->next;
+ }
}
-void
-soup_message_remove_handler (SoupMessage *msg,
- SoupHandlerType type,
- SoupCallbackFn handler_cb,
- gpointer user_data)
+void
+soup_message_remove_handler_by_func_and_data (SoupMessage *msg,
+ SoupHandlerFn handler_cb,
+ gpointer user_data)
{
- GSList *iter = msg->priv->content_handlers;
+ GSList *iter;
+ g_return_if_fail (msg != NULL);
+ g_return_if_fail (handler_cb != NULL);
+
+ iter = msg->priv->content_handlers;
while (iter) {
SoupHandlerData *data = iter->data;
- if (data->handler_cb == handler_cb &&
- data->user_data == user_data &&
- data->type == type) {
- msg->priv->content_handlers =
- g_slist_remove_link (
- msg->priv->content_handlers,
- iter);
- g_free (data);
+ if (data->handler_cb == handler_cb &&
+ data->user_data == user_data) {
+ handler_free (data);
break;
}
-
+
iter = iter->next;
}
}