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, error, req->priv->user_data);
190 /* Free it only if callback exist, its probably a sync call */
191 if (req->status != SOUP_STATUS_QUEUED)
192 soup_message_free (req);
197 * soup_message_cancel:
198 * @req: a %SoupMessage currently being processed.
200 * Cancel a running message, and issue completion callback with a
201 * %SoupTransferStatus of %SOUP_ERROR_CANCELLED. If not requeued by the
202 * completion callback, the @msg will be destroyed.
205 soup_message_cancel (SoupMessage *req)
207 soup_message_issue_callback (req, SOUP_ERROR_CANCELLED);
211 soup_message_set_header (GHashTable **hash,
216 *hash = g_hash_table_new (soup_str_case_hash,
217 soup_str_case_equal);
219 g_hash_table_insert (*hash, g_strdup (name), g_strdup (value));
223 * soup_message_set_request_header:
224 * @req: a %SoupMessage.
225 * @name: header name.
226 * @value: header value.
228 * Adds a new transport header to be sent on an outgoing request.
231 soup_message_set_request_header (SoupMessage *req,
235 g_return_if_fail (req != NULL);
236 g_return_if_fail (name != NULL || name [0] != '\0');
238 if (req->priv->req_header) {
239 g_string_free (req->priv->req_header, TRUE);
240 req->priv->req_header = NULL;
243 soup_message_set_header (&req->request_headers, name, value);
247 * soup_message_get_request_header:
248 * @req: a %SoupMessage.
249 * @name: header name.
251 * Lookup the transport request header with a key equal to @name.
253 * Return value: the header's value or NULL if not found.
256 soup_message_get_request_header (SoupMessage *req,
259 g_return_val_if_fail (req != NULL, NULL);
260 g_return_val_if_fail (name != NULL || name [0] != '\0', NULL);
262 return req->request_headers ?
263 g_hash_table_lookup (req->request_headers, name) : NULL;
267 * soup_message_set_response_header:
268 * @req: a %SoupMessage.
269 * @name: header name.
270 * @value: header value.
272 * Adds a new transport header to be sent on an outgoing response.
275 soup_message_set_response_header (SoupMessage *req,
279 g_return_if_fail (req != NULL);
280 g_return_if_fail (name != NULL || name [0] != '\0');
282 soup_message_set_header (&req->response_headers, name, value);
286 * soup_message_get_response_header:
287 * @req: a %SoupMessage.
288 * @name: header name.
290 * Lookup the transport response header with a key equal to @name.
292 * Return value: the header's value or NULL if not found.
295 soup_message_get_response_header (SoupMessage *req,
298 g_return_val_if_fail (req != NULL, NULL);
299 g_return_val_if_fail (name != NULL || name [0] != '\0', NULL);
301 return req->response_headers ?
302 g_hash_table_lookup (req->response_headers, name) : NULL;
307 * @msg: a %SoupMessage.
309 * Syncronously send @msg. This call will not return until the transfer is
310 * finished successfully or there is an unrecoverable error.
312 * @msg is not free'd upon return.
314 * Return value: the %SoupErrorCode of the error encountered while sending, or
318 soup_message_send (SoupMessage *msg)
320 soup_message_queue (msg, NULL, NULL);
323 g_main_iteration (TRUE);
324 if (msg->status == SOUP_STATUS_FINISHED ||
325 msg->priv->errorcode != SOUP_ERROR_NONE)
326 return msg->priv->errorcode;
329 return SOUP_ERROR_NONE;
333 soup_message_set_flags (SoupMessage *msg, guint flags)
335 msg->priv->msg_flags = flags;
339 soup_message_get_flags (SoupMessage *msg)
341 return msg->priv->msg_flags;