1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * soup-message.c: Asyncronous Callback-based SOAP Request Queue.
6 * Alex Graveley (alex@helixcode.com)
8 * Copyright (C) 2000, Helix Code, Inc.
11 #include "soup-message.h"
12 #include "soup-misc.h"
13 #include "soup-context.h"
14 #include "soup-private.h"
15 #include "soup-transfer.h"
18 authorize_handler (SoupMessage *msg, gboolean proxy)
20 const char *auth_header;
24 ctx = proxy ? soup_get_proxy () : msg->context;
27 soup_message_get_response_header (
29 proxy ? "Proxy-Authenticate" : "WWW-Authenticate");
30 if (!auth_header) return SOUP_ERROR_CANT_AUTHENTICATE;
32 auth = soup_auth_new_from_header (ctx, auth_header);
33 if (!auth) return SOUP_ERROR_MALFORMED_HEADER;
36 if (soup_auth_invalidates_prior (auth))
37 soup_auth_free (ctx->auth);
39 soup_auth_free (auth);
40 return SOUP_ERROR_CANT_AUTHENTICATE;
46 if (msg->priv->req_header) {
47 g_string_free (msg->priv->req_header, TRUE);
48 msg->priv->req_header = NULL;
51 soup_message_queue (msg, msg->priv->callback, msg->priv->user_data);
53 return SOUP_ERROR_NONE;
57 redirect_handler (SoupMessage *msg, gpointer user_data)
61 switch (msg->response_code) {
62 case 300: /* Multiple Choices */
63 case 301: /* Moved Permanently */
64 case 302: /* Moved Temporarily */
65 case 303: /* See Other */
66 case 305: /* Use Proxy */
69 return SOUP_ERROR_NONE;
72 if (msg->priv->msg_flags & SOUP_MESSAGE_NO_REDIRECT)
73 return SOUP_ERROR_NONE;
75 new_url = soup_message_get_response_header (msg, "Location");
78 SoupContext *new_ctx = soup_context_get (new_url);
79 if (!new_ctx) return SOUP_ERROR_MALFORMED_HEADER;
81 soup_context_unref (msg->context);
82 msg->context = new_ctx;
84 soup_message_queue (msg,
86 msg->priv->user_data);
89 return SOUP_ERROR_NONE;
94 * @context: a %SoupContext for the destination endpoint.
95 * @action: a string which will be used as the SOAPAction header for the created
98 * Creates a new empty %SoupMessage, which will connect to the URL represented
99 * by @context. The new message has a status of @SOUP_STATUS_IDLE.
101 * Return value: the new %SoupMessage.
104 soup_message_new (SoupContext *context, SoupAction action)
108 g_return_val_if_fail (context, NULL);
110 ret = g_new0 (SoupMessage, 1);
111 ret->priv = g_new0 (SoupMessagePrivate, 1);
112 ret->status = SOUP_STATUS_IDLE;
113 ret->action = g_strdup (action);
114 ret->context = context;
115 ret->method = SOUP_METHOD_POST;
117 soup_context_ref (context);
120 * Add a 401 (Authorization Required) response code handler if the
121 * context URI has a login user name.
123 if (soup_context_get_uri (context)->user)
124 soup_message_add_response_code_handler (
127 SOUP_HANDLER_POST_BODY,
128 (SoupHandlerFn) authorize_handler,
129 GINT_TO_POINTER (FALSE));
132 * Always add a 407 (Proxy-Authorization Required) handler, in case the
133 * proxy is reset after message creation.
135 soup_message_add_response_code_handler (
138 SOUP_HANDLER_POST_BODY,
139 (SoupHandlerFn) authorize_handler,
140 GINT_TO_POINTER (TRUE));
143 * Handle redirect response codes 300, 301, 302, 303, and 305.
145 soup_message_add_header_handler (ret,
147 SOUP_HANDLER_PRE_BODY,
155 * soup_message_new_full:
156 * @context: a %SoupContext for the destination endpoint.
157 * @action: a string which will be used as the SOAPAction header for the created
159 * @req_owner: the %SoupOwnership of the passed data buffer.
160 * @req_body: a data buffer containing the body of the message request.
161 * @req_length: the byte length of @req_body.
163 * Creates a new %SoupMessage, which will connect to the URL represented by
164 * @context. The new message has a status of @SOUP_STATUS_IDLE. The request data
165 * buffer will be filled from @req_owner, @req_body, and @req_length
168 * Return value: the new %SoupMessage.
171 soup_message_new_full (SoupContext *context,
173 SoupOwnership req_owner,
177 SoupMessage *ret = soup_message_new (context, action);
179 ret->request.owner = req_owner;
180 ret->request.body = req_body;
181 ret->request.length = req_length;
187 * soup_message_cleanup:
188 * @req: a %SoupMessage.
189 * @action: a string which will be used as the SOAPAction header for the created
192 * Frees any temporary resources created in the processing of @req. Request and
193 * response data buffers are left intact.
196 soup_message_cleanup (SoupMessage *req)
198 g_return_if_fail (req != NULL);
200 if (req->priv->read_tag) {
201 soup_transfer_read_cancel (req->priv->read_tag);
202 req->priv->read_tag = 0;
205 if (req->priv->write_tag) {
206 soup_transfer_write_cancel (req->priv->write_tag);
207 req->priv->write_tag = 0;
210 if (req->priv->connect_tag) {
211 soup_context_cancel_connect (req->priv->connect_tag);
212 req->priv->connect_tag = NULL;
214 if (req->priv->conn) {
215 soup_connection_release (req->priv->conn);
216 req->priv->conn = NULL;
219 soup_active_requests = g_slist_remove (soup_active_requests, req);
223 soup_message_remove_header (gchar *name, gchar *value, gpointer unused)
231 * @req: a %SoupMessage to destroy.
233 * Destroys the %SoupMessage pointed to by @req. Request and response headers
234 * are freed. Request and response data buffers are also freed if their
235 * ownership is %SOUP_BUFFER_SYSTEM_OWNED. The message's destination context
236 * will be de-referenced.
239 soup_message_free (SoupMessage *req)
241 g_return_if_fail (req != NULL);
243 soup_message_cleanup (req);
245 soup_context_unref (req->context);
247 if (req->request.owner == SOUP_BUFFER_SYSTEM_OWNED)
248 g_free (req->request.body);
249 if (req->response.owner == SOUP_BUFFER_SYSTEM_OWNED)
250 g_free (req->response.body);
252 if (req->priv->req_header)
253 g_string_free (req->priv->req_header, TRUE);
255 if (req->request_headers) {
256 g_hash_table_foreach (req->request_headers,
257 (GHFunc) soup_message_remove_header,
259 g_hash_table_destroy (req->request_headers);
262 if (req->response_headers) {
263 g_hash_table_foreach (req->response_headers,
264 (GHFunc) soup_message_remove_header,
266 g_hash_table_destroy (req->response_headers);
269 g_slist_foreach (req->priv->content_handlers, (GFunc) g_free, NULL);
270 g_slist_free (req->priv->content_handlers);
273 g_free (req->action);
278 * soup_message_issue_callback:
279 * @req: a %SoupMessage currently being processed.
280 * @error: a %SoupErrorCode to be passed to %req's completion callback.
282 * Finalizes the message request, by first freeing any temporary resources, then
283 * issuing the callback function pointer passed in %soup_message_new or
284 * %soup_message_new_full. If, after returning from the callback, the message
285 * has not been requeued, @msg is destroyed using %soup_message_free.
288 soup_message_issue_callback (SoupMessage *req, SoupErrorCode error)
290 g_return_if_fail (req != NULL);
293 * Make sure we don't have some icky recursion if the callback
294 * runs the main loop, and the connection has some data or error
295 * which causes the callback to be run again.
297 soup_message_cleanup (req);
299 req->priv->errorcode = error;
301 if (req->priv->callback) {
302 (*req->priv->callback) (req, error, req->priv->user_data);
304 if (req->status != SOUP_STATUS_QUEUED)
305 soup_message_free (req);
310 * soup_message_cancel:
311 * @req: a %SoupMessage currently being processed.
313 * Cancel a running message, and issue completion callback with a
314 * %SoupTransferStatus of %SOUP_ERROR_CANCELLED. If not requeued by the
315 * completion callback, the @msg will be destroyed.
318 soup_message_cancel (SoupMessage *req)
320 soup_message_issue_callback (req, SOUP_ERROR_CANCELLED);
324 soup_message_set_header (GHashTable **hash,
328 gpointer old_name, old_value;
331 *hash = g_hash_table_new (soup_str_case_hash,
332 soup_str_case_equal);
333 else if (g_hash_table_lookup_extended (*hash,
337 g_hash_table_remove (*hash, name);
343 g_hash_table_insert (*hash, g_strdup (name), g_strdup (value));
347 * soup_message_set_request_header:
348 * @req: a %SoupMessage.
349 * @name: header name.
350 * @value: header value.
352 * Adds a new transport header to be sent on an outgoing request. Passing a NULL
353 * @value will remove the header name supplied.
356 soup_message_set_request_header (SoupMessage *req,
360 g_return_if_fail (req != NULL);
361 g_return_if_fail (name != NULL || name [0] != '\0');
363 if (req->priv->req_header) {
364 g_string_free (req->priv->req_header, TRUE);
365 req->priv->req_header = NULL;
368 soup_message_set_header (&req->request_headers, name, value);
372 * soup_message_get_request_header:
373 * @req: a %SoupMessage.
374 * @name: header name.
376 * Lookup the transport request header with a key equal to @name.
378 * Return value: the header's value or NULL if not found.
381 soup_message_get_request_header (SoupMessage *req,
384 g_return_val_if_fail (req != NULL, NULL);
385 g_return_val_if_fail (name != NULL || name [0] != '\0', NULL);
387 return req->request_headers ?
388 g_hash_table_lookup (req->request_headers, name) : NULL;
392 * soup_message_set_response_header:
393 * @req: a %SoupMessage.
394 * @name: header name.
395 * @value: header value.
397 * Adds a new transport header to be sent on an outgoing response. Passing a
398 * NULL @value will remove the header name supplied.
401 soup_message_set_response_header (SoupMessage *req,
405 g_return_if_fail (req != NULL);
406 g_return_if_fail (name != NULL || name [0] != '\0');
408 soup_message_set_header (&req->response_headers, name, value);
412 * soup_message_get_response_header:
413 * @req: a %SoupMessage.
414 * @name: header name.
416 * Lookup the transport response header with a key equal to @name.
418 * Return value: the header's value or NULL if not found.
421 soup_message_get_response_header (SoupMessage *req,
424 g_return_val_if_fail (req != NULL, NULL);
425 g_return_val_if_fail (name != NULL || name [0] != '\0', NULL);
427 return req->response_headers ?
428 g_hash_table_lookup (req->response_headers, name) : NULL;
433 * @msg: a %SoupMessage.
435 * Syncronously send @msg. This call will not return until the transfer is
436 * finished successfully or there is an unrecoverable error.
438 * @msg is not free'd upon return.
440 * Return value: the %SoupErrorCode of the error encountered while sending, or
444 soup_message_send (SoupMessage *msg)
446 soup_message_queue (msg, NULL, NULL);
449 g_main_iteration (TRUE);
450 if (msg->status == SOUP_STATUS_FINISHED ||
451 msg->priv->errorcode != SOUP_ERROR_NONE)
452 return msg->priv->errorcode;
455 return SOUP_ERROR_NONE;
459 soup_message_set_method (SoupMessage *msg, const gchar *method)
461 g_return_if_fail (msg != NULL);
462 g_return_if_fail (method != NULL);
464 msg->method = method;
468 soup_message_get_method (SoupMessage *msg)
470 g_return_val_if_fail (msg != NULL, NULL);
476 RESPONSE_HEADER_HANDLER,
477 RESPONSE_CODE_HANDLER,
478 RESPONSE_BODY_HANDLER
482 SoupHandlerType type;
483 SoupHandlerFn handler_cb;
486 SoupHandlerKind kind;
492 soup_message_add_handler (SoupMessage *msg,
493 SoupHandlerType type,
494 SoupHandlerFn handler_cb,
496 SoupHandlerKind kind,
500 SoupHandlerData *data;
502 data = g_new0 (SoupHandlerData, 1);
504 data->handler_cb = handler_cb;
505 data->user_data = user_data;
507 data->header = header;
510 msg->priv->content_handlers =
511 g_slist_append (msg->priv->content_handlers, data);
515 soup_message_add_header_handler (SoupMessage *msg,
517 SoupHandlerType type,
518 SoupHandlerFn handler_cb,
521 g_return_if_fail (msg != NULL);
522 g_return_if_fail (header != NULL);
523 g_return_if_fail (handler_cb != NULL);
525 soup_message_add_handler (msg,
529 RESPONSE_HEADER_HANDLER,
535 soup_message_add_response_code_handler (SoupMessage *msg,
537 SoupHandlerType type,
538 SoupHandlerFn handler_cb,
541 g_return_if_fail (msg != NULL);
542 g_return_if_fail (code != 0);
543 g_return_if_fail (handler_cb != NULL);
545 soup_message_add_handler (msg,
549 RESPONSE_CODE_HANDLER,
555 soup_message_add_body_handler (SoupMessage *msg,
556 SoupHandlerType type,
557 SoupHandlerFn handler_cb,
560 g_return_if_fail (msg != NULL);
561 g_return_if_fail (handler_cb != NULL);
563 soup_message_add_handler (msg,
567 RESPONSE_BODY_HANDLER,
573 soup_message_run_handlers (SoupMessage *msg, SoupHandlerType invoke_type)
576 SoupErrorCode retval = SOUP_ERROR_NONE;
578 g_return_val_if_fail (msg != NULL, retval);
580 for (list = msg->priv->content_handlers; list; list = list->next) {
581 SoupHandlerData *data = list->data;
583 if (data->type != invoke_type) continue;
585 switch (data->kind) {
586 case RESPONSE_HEADER_HANDLER:
587 if (!soup_message_get_response_header (msg,
591 case RESPONSE_CODE_HANDLER:
592 if (msg->response_code != data->code) continue;
594 case RESPONSE_BODY_HANDLER:
598 retval = (*data->handler_cb) (msg, data->user_data);
600 if (retval != SOUP_ERROR_NONE) break;
601 if (msg->status == SOUP_STATUS_QUEUED) break;
608 soup_message_remove_handler (SoupMessage *msg,
609 SoupHandlerFn handler_cb,
612 GSList *iter = msg->priv->content_handlers;
615 SoupHandlerData *data = iter->data;
617 if (data->handler_cb == handler_cb &&
618 data->user_data == user_data) {
619 msg->priv->content_handlers =
620 g_slist_remove_link (
621 msg->priv->content_handlers,
631 static inline gboolean
632 ADDED_FLAG (SoupMessage *msg, guint newflags, SoupMessageFlags find)
634 return ((newflags & find) && !(msg->priv->msg_flags & find));
637 static inline gboolean
638 REMOVED_FLAG (SoupMessage *msg, guint newflags, SoupMessageFlags find)
640 return (!(newflags & find) && (msg->priv->msg_flags & find));
644 soup_message_set_flags (SoupMessage *msg, guint flags)
646 g_return_if_fail (msg != NULL);
648 if (ADDED_FLAG (msg, flags, SOUP_MESSAGE_NO_REDIRECT))
649 soup_message_remove_handler (msg,
652 else if (REMOVED_FLAG (msg, flags, SOUP_MESSAGE_NO_REDIRECT))
653 soup_message_add_header_handler (msg,
655 SOUP_HANDLER_PRE_BODY,
659 msg->priv->msg_flags = flags;
663 soup_message_get_flags (SoupMessage *msg)
665 return msg->priv->msg_flags;