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-context.h"
13 #include "soup-private.h"
17 * @context: a %SoupContext for the destination endpoint.
18 * @action: a string which will be used as the SOAPAction header for the created
21 * Creates a new empty %SoupMessage, which will connect to the URL represented
22 * by @context. The new message has a status of @SOUP_STATUS_IDLE.
24 * Return value: the new %SoupMessage.
27 soup_message_new (SoupContext *context, SoupAction action)
30 ret = g_new0 (SoupMessage, 1);
31 ret->priv = g_new0 (SoupMessagePrivate, 1);
32 ret->status = SOUP_STATUS_IDLE;
33 ret->action = g_strdup (action);
34 ret->context = context;
36 soup_context_ref (context);
42 * soup_message_new_full:
43 * @context: a %SoupContext for the destination endpoint.
44 * @action: a string which will be used as the SOAPAction header for the created
46 * @req_owner: the %SoupOwnership of the passed data buffer.
47 * @req_body: a data buffer containing the body of the message request.
48 * @req_length: the byte length of @req_body.
50 * Creates a new %SoupMessage, which will connect to the URL represented by
51 * @context. The new message has a status of @SOUP_STATUS_IDLE. The request data
52 * buffer will be filled from @req_owner, @req_body, and @req_length
55 * Return value: the new %SoupMessage.
58 soup_message_new_full (SoupContext *context,
60 SoupOwnership req_owner,
64 SoupMessage *ret = soup_message_new (context, action);
66 ret->request.owner = req_owner;
67 ret->request.body = req_body;
68 ret->request.length = req_length;
73 #define source_remove(_src) \
74 ({ if ((_src)) { g_source_remove ((_src)); (_src) = 0; }})
77 * soup_message_cleanup:
78 * @req: a %SoupMessage.
79 * @action: a string which will be used as the SOAPAction header for the created
82 * Frees any temporary resources created in the processing of @req. Request and
83 * response data buffers are left intact.
86 soup_message_cleanup (SoupMessage *req)
88 g_return_if_fail (req != NULL);
90 source_remove (req->priv->read_tag);
91 source_remove (req->priv->write_tag);
92 source_remove (req->priv->error_tag);
93 source_remove (req->priv->timeout_tag);
95 if (req->priv->connect_tag)
96 soup_context_cancel_connect (req->priv->connect_tag);
98 soup_connection_release (req->priv->conn);
100 req->priv->connect_tag = NULL;
101 req->priv->conn = NULL;
102 req->priv->write_len = 0;
103 req->priv->header_len = 0;
104 req->priv->content_length = 0;
105 req->priv->is_chunked = FALSE;
107 soup_active_requests = g_slist_remove (soup_active_requests, req);
111 soup_message_remove_header (gchar *name, gchar *value, gpointer unused)
119 * @req: a %SoupMessage to destroy.
121 * Destroys the %SoupMessage pointed to by @req. Request and response headers
122 * are freed. Request and response data buffers are also freed if their
123 * ownership is %SOUP_BUFFER_SYSTEM_OWNED. The message's destination context
124 * will be de-referenced.
127 soup_message_free (SoupMessage *req)
129 g_return_if_fail (req != NULL);
131 soup_message_cleanup (req);
133 soup_context_unref (req->context);
135 if (req->request.owner == SOUP_BUFFER_SYSTEM_OWNED)
136 g_free (req->request.body);
137 if (req->response.owner == SOUP_BUFFER_SYSTEM_OWNED)
138 g_free (req->response.body);
140 if (req->priv->req_header)
141 g_string_free (req->priv->req_header, TRUE);
143 if (req->request_headers) {
144 g_hash_table_foreach (req->request_headers,
145 (GHFunc) soup_message_remove_header,
147 g_hash_table_destroy (req->request_headers);
150 if (req->response_headers) {
151 g_hash_table_foreach (req->response_headers,
152 (GHFunc) soup_message_remove_header,
154 g_hash_table_destroy (req->response_headers);
157 if (req->priv->recv_buf)
158 g_byte_array_free (req->priv->recv_buf, TRUE);
161 g_free (req->action);
166 * soup_message_issue_callback:
167 * @req: a %SoupMessage currently being processed.
168 * @error: a %SoupErrorCode to be passed to %req's completion callback.
170 * Finalizes the message request, by first freeing any temporary resources, then
171 * issuing the callback function pointer passed in %soup_message_new or
172 * %soup_message_new_full. If, after returning from the callback, the message
173 * has not been requeued, @msg is destroyed using %soup_message_free.
176 soup_message_issue_callback (SoupMessage *req, SoupErrorCode error)
178 g_return_if_fail (req != NULL);
180 /* make sure we don't have some icky recursion if the callback
181 runs the main loop, and the connection has some data or error
182 which causes the callback to be run again */
183 soup_message_cleanup (req);
185 req->priv->errorcode = error;
187 if (req->priv->callback)
188 (*req->priv->callback) (req,
190 req->priv->user_data);
192 if (req->status != SOUP_STATUS_QUEUED) soup_message_free (req);
196 * soup_message_cancel:
197 * @req: a %SoupMessage currently being processed.
199 * Cancel a running message, and issue completion callback with a
200 * %SoupTransferStatus of %SOUP_ERROR_CANCELLED. If not requeued by the
201 * completion callback, the @msg will be destroyed.
204 soup_message_cancel (SoupMessage *req)
206 soup_message_issue_callback (req, SOUP_ERROR_CANCELLED);
210 soup_message_set_header (GHashTable **hash,
215 *hash = g_hash_table_new (soup_str_case_hash,
216 soup_str_case_equal);
218 g_hash_table_insert (*hash, g_strdup (name), g_strdup (value));
222 * soup_message_set_request_header:
223 * @req: a %SoupMessage.
224 * @name: header name.
225 * @value: header value.
227 * Adds a new transport header to be sent on an outgoing request.
230 soup_message_set_request_header (SoupMessage *req,
234 g_return_if_fail (req != NULL);
235 g_return_if_fail (name != NULL || name [0] != '\0');
237 if (req->priv->req_header) {
238 g_string_free (req->priv->req_header, TRUE);
239 req->priv->req_header = NULL;
242 soup_message_set_header (&req->request_headers, name, value);
246 * soup_message_get_request_header:
247 * @req: a %SoupMessage.
248 * @name: header name.
250 * Lookup the transport request header with a key equal to @name.
252 * Return value: the header's value or NULL if not found.
255 soup_message_get_request_header (SoupMessage *req,
258 g_return_val_if_fail (req != NULL, NULL);
259 g_return_val_if_fail (name != NULL || name [0] != '\0', NULL);
261 return req->request_headers ?
262 g_hash_table_lookup (req->request_headers, name) : NULL;
266 * soup_message_set_response_header:
267 * @req: a %SoupMessage.
268 * @name: header name.
269 * @value: header value.
271 * Adds a new transport header to be sent on an outgoing response.
274 soup_message_set_response_header (SoupMessage *req,
278 g_return_if_fail (req != NULL);
279 g_return_if_fail (name != NULL || name [0] != '\0');
281 soup_message_set_header (&req->response_headers, name, value);
285 * soup_message_get_response_header:
286 * @req: a %SoupMessage.
287 * @name: header name.
289 * Lookup the transport response header with a key equal to @name.
291 * Return value: the header's value or NULL if not found.
294 soup_message_get_response_header (SoupMessage *req,
297 g_return_val_if_fail (req != NULL, NULL);
298 g_return_val_if_fail (name != NULL || name [0] != '\0', NULL);
300 return req->response_headers ?
301 g_hash_table_lookup (req->response_headers, name) : NULL;
306 * @msg: a %SoupMessage.
308 * Syncronously send @msg. This call will not return until the transfer is
309 * finished successfully or there is an unrecoverable error.
311 * Return value: the %SoupErrorCode of the error encountered while sending, or
315 soup_message_send (SoupMessage *msg)
317 soup_message_queue (msg, NULL, NULL);
320 g_main_iteration (TRUE);
321 if (msg->status == SOUP_STATUS_FINISHED ||
322 msg->priv->errorcode != SOUP_ERROR_NONE)
323 return msg->priv->errorcode;
326 return SOUP_ERROR_NONE;
330 soup_message_set_flags (SoupMessage *msg, guint flags)
332 msg->priv->msg_flags = flags;
336 soup_message_get_flags (SoupMessage *msg)
338 return msg->priv->msg_flags;