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 ret->priv->http_version = SOUP_HTTP_1_1;
119 soup_context_ref (context);
122 * Add a 401 (Authorization Required) response code handler if the
123 * context URI has a login user name.
125 if (soup_context_get_uri (context)->user)
126 soup_message_add_response_code_handler (
129 SOUP_HANDLER_POST_BODY,
130 (SoupHandlerFn) authorize_handler,
131 GINT_TO_POINTER (FALSE));
134 * Always add a 407 (Proxy-Authorization Required) handler, in case the
135 * proxy is reset after message creation.
137 soup_message_add_response_code_handler (
140 SOUP_HANDLER_POST_BODY,
141 (SoupHandlerFn) authorize_handler,
142 GINT_TO_POINTER (TRUE));
145 * Handle redirect response codes 300, 301, 302, 303, and 305.
147 soup_message_add_header_handler (ret,
149 SOUP_HANDLER_PRE_BODY,
157 * soup_message_new_full:
158 * @context: a %SoupContext for the destination endpoint.
159 * @action: a string which will be used as the SOAPAction header for the created
161 * @req_owner: the %SoupOwnership of the passed data buffer.
162 * @req_body: a data buffer containing the body of the message request.
163 * @req_length: the byte length of @req_body.
165 * Creates a new %SoupMessage, which will connect to the URL represented by
166 * @context. The new message has a status of @SOUP_STATUS_IDLE. The request data
167 * buffer will be filled from @req_owner, @req_body, and @req_length
170 * Return value: the new %SoupMessage.
173 soup_message_new_full (SoupContext *context,
175 SoupOwnership req_owner,
179 SoupMessage *ret = soup_message_new (context, action);
181 ret->request.owner = req_owner;
182 ret->request.body = req_body;
183 ret->request.length = req_length;
189 * soup_message_cleanup:
190 * @req: a %SoupMessage.
191 * @action: a string which will be used as the SOAPAction header for the created
194 * Frees any temporary resources created in the processing of @req. Request and
195 * response data buffers are left intact.
198 soup_message_cleanup (SoupMessage *req)
200 g_return_if_fail (req != NULL);
202 if (req->priv->read_tag) {
203 soup_transfer_read_cancel (req->priv->read_tag);
204 req->priv->read_tag = 0;
207 if (req->priv->write_tag) {
208 soup_transfer_write_cancel (req->priv->write_tag);
209 req->priv->write_tag = 0;
212 if (req->priv->connect_tag) {
213 soup_context_cancel_connect (req->priv->connect_tag);
214 req->priv->connect_tag = NULL;
216 if (req->priv->conn) {
217 soup_connection_release (req->priv->conn);
218 req->priv->conn = NULL;
221 soup_active_requests = g_slist_remove (soup_active_requests, req);
225 soup_message_remove_header (gchar *name, gchar *value, gpointer unused)
233 * @req: a %SoupMessage to destroy.
235 * Destroys the %SoupMessage pointed to by @req. Request and response headers
236 * are freed. Request and response data buffers are also freed if their
237 * ownership is %SOUP_BUFFER_SYSTEM_OWNED. The message's destination context
238 * will be de-referenced.
241 soup_message_free (SoupMessage *req)
243 g_return_if_fail (req != NULL);
245 soup_message_cleanup (req);
247 soup_context_unref (req->context);
249 if (req->request.owner == SOUP_BUFFER_SYSTEM_OWNED)
250 g_free (req->request.body);
251 if (req->response.owner == SOUP_BUFFER_SYSTEM_OWNED)
252 g_free (req->response.body);
254 if (req->priv->req_header)
255 g_string_free (req->priv->req_header, TRUE);
257 if (req->request_headers) {
258 g_hash_table_foreach (req->request_headers,
259 (GHFunc) soup_message_remove_header,
261 g_hash_table_destroy (req->request_headers);
264 if (req->response_headers) {
265 g_hash_table_foreach (req->response_headers,
266 (GHFunc) soup_message_remove_header,
268 g_hash_table_destroy (req->response_headers);
271 g_slist_foreach (req->priv->content_handlers, (GFunc) g_free, NULL);
272 g_slist_free (req->priv->content_handlers);
275 g_free (req->action);
280 * soup_message_issue_callback:
281 * @req: a %SoupMessage currently being processed.
282 * @error: a %SoupErrorCode to be passed to %req's completion callback.
284 * Finalizes the message request, by first freeing any temporary resources, then
285 * issuing the callback function pointer passed in %soup_message_new or
286 * %soup_message_new_full. If, after returning from the callback, the message
287 * has not been requeued, @msg is destroyed using %soup_message_free.
290 soup_message_issue_callback (SoupMessage *req, SoupErrorCode error)
292 g_return_if_fail (req != NULL);
295 * Make sure we don't have some icky recursion if the callback
296 * runs the main loop, and the connection has some data or error
297 * which causes the callback to be run again.
299 soup_message_cleanup (req);
301 req->priv->errorcode = error;
303 if (req->priv->callback) {
304 (*req->priv->callback) (req, error, req->priv->user_data);
306 if (req->status != SOUP_STATUS_QUEUED)
307 soup_message_free (req);
312 * soup_message_cancel:
313 * @req: a %SoupMessage currently being processed.
315 * Cancel a running message, and issue completion callback with a
316 * %SoupTransferStatus of %SOUP_ERROR_CANCELLED. If not requeued by the
317 * completion callback, the @msg will be destroyed.
320 soup_message_cancel (SoupMessage *req)
322 soup_message_issue_callback (req, SOUP_ERROR_CANCELLED);
326 soup_message_set_header (GHashTable **hash,
330 gpointer old_name, old_value;
333 *hash = g_hash_table_new (soup_str_case_hash,
334 soup_str_case_equal);
335 else if (g_hash_table_lookup_extended (*hash,
339 g_hash_table_remove (*hash, name);
345 g_hash_table_insert (*hash, g_strdup (name), g_strdup (value));
349 * soup_message_set_request_header:
350 * @req: a %SoupMessage.
351 * @name: header name.
352 * @value: header value.
354 * Adds a new transport header to be sent on an outgoing request. Passing a NULL
355 * @value will remove the header name supplied.
358 soup_message_set_request_header (SoupMessage *req,
362 g_return_if_fail (req != NULL);
363 g_return_if_fail (name != NULL || name [0] != '\0');
365 if (req->priv->req_header) {
366 g_string_free (req->priv->req_header, TRUE);
367 req->priv->req_header = NULL;
370 soup_message_set_header (&req->request_headers, name, value);
374 * soup_message_get_request_header:
375 * @req: a %SoupMessage.
376 * @name: header name.
378 * Lookup the transport request header with a key equal to @name.
380 * Return value: the header's value or NULL if not found.
383 soup_message_get_request_header (SoupMessage *req,
386 g_return_val_if_fail (req != NULL, NULL);
387 g_return_val_if_fail (name != NULL || name [0] != '\0', NULL);
389 return req->request_headers ?
390 g_hash_table_lookup (req->request_headers, name) : NULL;
394 * soup_message_set_response_header:
395 * @req: a %SoupMessage.
396 * @name: header name.
397 * @value: header value.
399 * Adds a new transport header to be sent on an outgoing response. Passing a
400 * NULL @value will remove the header name supplied.
403 soup_message_set_response_header (SoupMessage *req,
407 g_return_if_fail (req != NULL);
408 g_return_if_fail (name != NULL || name [0] != '\0');
410 soup_message_set_header (&req->response_headers, name, value);
414 * soup_message_get_response_header:
415 * @req: a %SoupMessage.
416 * @name: header name.
418 * Lookup the transport response header with a key equal to @name.
420 * Return value: the header's value or NULL if not found.
423 soup_message_get_response_header (SoupMessage *req,
426 g_return_val_if_fail (req != NULL, NULL);
427 g_return_val_if_fail (name != NULL || name [0] != '\0', NULL);
429 return req->response_headers ?
430 g_hash_table_lookup (req->response_headers, name) : NULL;
435 * @msg: a %SoupMessage.
437 * Syncronously send @msg. This call will not return until the transfer is
438 * finished successfully or there is an unrecoverable error.
440 * @msg is not free'd upon return.
442 * Return value: the %SoupErrorCode of the error encountered while sending, or
446 soup_message_send (SoupMessage *msg)
448 soup_message_queue (msg, NULL, NULL);
451 g_main_iteration (TRUE);
452 if (msg->status == SOUP_STATUS_FINISHED ||
453 msg->priv->errorcode != SOUP_ERROR_NONE)
454 return msg->priv->errorcode;
457 return SOUP_ERROR_NONE;
461 soup_message_set_method (SoupMessage *msg, const gchar *method)
463 g_return_if_fail (msg != NULL);
464 g_return_if_fail (method != NULL);
466 msg->method = method;
470 soup_message_get_method (SoupMessage *msg)
472 g_return_val_if_fail (msg != NULL, NULL);
478 RESPONSE_HEADER_HANDLER,
479 RESPONSE_CODE_HANDLER,
480 RESPONSE_BODY_HANDLER
484 SoupHandlerType type;
485 SoupHandlerFn handler_cb;
488 SoupHandlerKind kind;
494 soup_message_add_handler (SoupMessage *msg,
495 SoupHandlerType type,
496 SoupHandlerFn handler_cb,
498 SoupHandlerKind kind,
502 SoupHandlerData *data;
504 data = g_new0 (SoupHandlerData, 1);
506 data->handler_cb = handler_cb;
507 data->user_data = user_data;
509 data->header = header;
512 msg->priv->content_handlers =
513 g_slist_append (msg->priv->content_handlers, data);
517 soup_message_add_header_handler (SoupMessage *msg,
519 SoupHandlerType type,
520 SoupHandlerFn handler_cb,
523 g_return_if_fail (msg != NULL);
524 g_return_if_fail (header != NULL);
525 g_return_if_fail (handler_cb != NULL);
527 soup_message_add_handler (msg,
531 RESPONSE_HEADER_HANDLER,
537 soup_message_add_response_code_handler (SoupMessage *msg,
539 SoupHandlerType type,
540 SoupHandlerFn handler_cb,
543 g_return_if_fail (msg != NULL);
544 g_return_if_fail (code != 0);
545 g_return_if_fail (handler_cb != NULL);
547 soup_message_add_handler (msg,
551 RESPONSE_CODE_HANDLER,
557 soup_message_add_body_handler (SoupMessage *msg,
558 SoupHandlerType type,
559 SoupHandlerFn handler_cb,
562 g_return_if_fail (msg != NULL);
563 g_return_if_fail (handler_cb != NULL);
565 soup_message_add_handler (msg,
569 RESPONSE_BODY_HANDLER,
575 soup_message_run_handlers (SoupMessage *msg, SoupHandlerType invoke_type)
578 SoupErrorCode retval = SOUP_ERROR_NONE;
580 g_return_val_if_fail (msg != NULL, retval);
582 for (list = msg->priv->content_handlers; list; list = list->next) {
583 SoupHandlerData *data = list->data;
585 if (data->type != invoke_type) continue;
587 switch (data->kind) {
588 case RESPONSE_HEADER_HANDLER:
589 if (!soup_message_get_response_header (msg,
593 case RESPONSE_CODE_HANDLER:
594 if (msg->response_code != data->code) continue;
596 case RESPONSE_BODY_HANDLER:
600 retval = (*data->handler_cb) (msg, data->user_data);
602 if (retval != SOUP_ERROR_NONE) break;
603 if (msg->status == SOUP_STATUS_QUEUED) break;
610 soup_message_remove_handler (SoupMessage *msg,
611 SoupHandlerFn handler_cb,
614 GSList *iter = msg->priv->content_handlers;
617 SoupHandlerData *data = iter->data;
619 if (data->handler_cb == handler_cb &&
620 data->user_data == user_data) {
621 msg->priv->content_handlers =
622 g_slist_remove_link (
623 msg->priv->content_handlers,
633 static inline gboolean
634 ADDED_FLAG (SoupMessage *msg, guint newflags, SoupMessageFlags find)
636 return ((newflags & find) && !(msg->priv->msg_flags & find));
639 static inline gboolean
640 REMOVED_FLAG (SoupMessage *msg, guint newflags, SoupMessageFlags find)
642 return (!(newflags & find) && (msg->priv->msg_flags & find));
646 soup_message_set_flags (SoupMessage *msg, guint flags)
648 g_return_if_fail (msg != NULL);
650 if (ADDED_FLAG (msg, flags, SOUP_MESSAGE_NO_REDIRECT))
651 soup_message_remove_handler (msg,
654 else if (REMOVED_FLAG (msg, flags, SOUP_MESSAGE_NO_REDIRECT))
655 soup_message_add_header_handler (msg,
657 SOUP_HANDLER_PRE_BODY,
661 msg->priv->msg_flags = flags;
665 soup_message_get_flags (SoupMessage *msg)
667 g_return_val_if_fail (msg != NULL, 0);
669 return msg->priv->msg_flags;
673 soup_message_set_http_version (SoupMessage *msg, SoupHttpVersion version)
675 g_return_if_fail (msg != NULL);
677 msg->priv->http_version = version;