gtk-doc fixups.
[platform/upstream/libsoup.git] / libsoup / soup-message.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * soup-message.c: Asyncronous Callback-based SOAP Request Queue.
4  *
5  * Authors:
6  *      Alex Graveley (alex@helixcode.com)
7  *
8  * Copyright (C) 2000, Helix Code, Inc.
9  */
10
11 #include "soup-message.h"
12 #include "soup-context.h"
13 #include "soup-private.h"
14
15 /**
16  * soup_message_new:
17  * @context: a %SoupContext for the destination endpoint.
18  * @action: a string which will be used as the SOAPAction header for the created
19  * request.
20  * 
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.
23  *
24  * Return value: the new %SoupMessage.
25  */
26 SoupMessage *
27 soup_message_new (SoupContext *context, SoupAction action) 
28 {
29         SoupMessage *ret;
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;
35
36         soup_context_ref (context);
37
38         return ret;
39 }
40
41 /**
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
45  * request.
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.
49  * 
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
53  * respectively.
54  *
55  * Return value: the new %SoupMessage.
56  */
57 SoupMessage *
58 soup_message_new_full (SoupContext   *context,
59                        SoupAction     action,
60                        SoupOwnership  req_owner,
61                        gchar         *req_body,
62                        gulong         req_length)
63 {
64         SoupMessage *ret = soup_message_new (context, action);
65
66         ret->request.owner = req_owner;
67         ret->request.body = req_body;
68         ret->request.length = req_length;
69
70         return ret;
71 }
72
73 #define source_remove(_src) \
74         ({ if ((_src)) { g_source_remove ((_src)); (_src) = 0; }})
75
76 /**
77  * soup_message_cleanup:
78  * @req: a %SoupMessage.
79  * @action: a string which will be used as the SOAPAction header for the created
80  * request.
81  * 
82  * Frees any temporary resources created in the processing of @req. Request and
83  * response data buffers are left intact.
84  */
85 void 
86 soup_message_cleanup (SoupMessage *req)
87 {
88         g_return_if_fail (req != NULL);
89
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);
94
95         if (req->priv->connect_tag) 
96                 soup_context_cancel_connect (req->priv->connect_tag);
97         if (req->priv->conn) 
98                 soup_connection_release (req->priv->conn);
99
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;
106
107         soup_active_requests = g_slist_remove (soup_active_requests, req);
108 }
109
110 static void
111 soup_message_remove_header (gchar *name, gchar *value, gpointer unused)
112 {
113         g_free (name);
114         g_free (value);
115 }
116
117 /**
118  * soup_message_free:
119  * @req: a %SoupMessage to destroy.
120  * 
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.
125  */
126 void 
127 soup_message_free (SoupMessage *req)
128 {
129         g_return_if_fail (req != NULL);
130
131         soup_message_cleanup (req);
132
133         soup_context_unref (req->context);
134
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);
139
140         if (req->priv->req_header) 
141                 g_string_free (req->priv->req_header, TRUE);
142
143         if (req->request_headers) {
144                 g_hash_table_foreach (req->request_headers,
145                                       (GHFunc) soup_message_remove_header,
146                                       NULL);
147                 g_hash_table_destroy (req->request_headers);
148         }
149
150         if (req->response_headers) {
151                 g_hash_table_foreach (req->response_headers,
152                                       (GHFunc) soup_message_remove_header,
153                                       NULL);
154                 g_hash_table_destroy (req->response_headers);
155         }
156
157         if (req->priv->recv_buf) 
158                 g_byte_array_free (req->priv->recv_buf, TRUE);
159
160         g_free (req->priv);
161         g_free (req->action);
162         g_free (req);
163 }
164
165 /**
166  * soup_message_issue_callback:
167  * @req: a %SoupMessage currently being processed.
168  * @error: a %SoupErrorCode to be passed to %req's completion callback.
169  * 
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.
174  */
175 void
176 soup_message_issue_callback (SoupMessage *req, SoupErrorCode error)
177 {
178         g_return_if_fail (req != NULL);
179
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);
184
185         req->priv->errorcode = error;
186
187         if (req->priv->callback) {
188                 (*req->priv->callback) (req, error, req->priv->user_data);
189
190                 /* Free it only if callback exist, its probably a sync call */
191                 if (req->status != SOUP_STATUS_QUEUED)
192                         soup_message_free (req);
193         }
194 }
195
196 /**
197  * soup_message_cancel:
198  * @req: a %SoupMessage currently being processed.
199  * 
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.
203  */
204 void 
205 soup_message_cancel (SoupMessage *req) 
206 {
207         soup_message_issue_callback (req, SOUP_ERROR_CANCELLED);
208 }
209
210 static void 
211 soup_message_set_header (GHashTable  **hash,
212                          const gchar  *name,
213                          const gchar  *value) 
214 {
215         if (!*hash) 
216                 *hash = g_hash_table_new (soup_str_case_hash, 
217                                           soup_str_case_equal);
218
219         g_hash_table_insert (*hash, g_strdup (name), g_strdup (value));
220 }
221
222 /**
223  * soup_message_set_request_header:
224  * @req: a %SoupMessage.
225  * @name: header name.
226  * @value: header value.
227  * 
228  * Adds a new transport header to be sent on an outgoing request.
229  */
230 void
231 soup_message_set_request_header (SoupMessage *req,
232                                  const gchar *name,
233                                  const gchar *value) 
234 {
235         g_return_if_fail (req != NULL);
236         g_return_if_fail (name != NULL || name [0] != '\0');
237
238         if (req->priv->req_header) {
239                 g_string_free (req->priv->req_header, TRUE);
240                 req->priv->req_header = NULL;
241         }
242
243         soup_message_set_header (&req->request_headers, name, value);
244 }
245
246 /**
247  * soup_message_get_request_header:
248  * @req: a %SoupMessage.
249  * @name: header name.
250  * 
251  * Lookup the transport request header with a key equal to @name.
252  *
253  * Return value: the header's value or NULL if not found.
254  */
255 const gchar *
256 soup_message_get_request_header (SoupMessage *req,
257                                  const gchar *name) 
258 {
259         g_return_val_if_fail (req != NULL, NULL);
260         g_return_val_if_fail (name != NULL || name [0] != '\0', NULL);
261
262         return req->request_headers ? 
263                 g_hash_table_lookup (req->request_headers, name) : NULL;
264 }
265
266 /**
267  * soup_message_set_response_header:
268  * @req: a %SoupMessage.
269  * @name: header name.
270  * @value: header value.
271  * 
272  * Adds a new transport header to be sent on an outgoing response.
273  */
274 void
275 soup_message_set_response_header (SoupMessage *req,
276                                   const gchar *name,
277                                   const gchar *value) 
278 {
279         g_return_if_fail (req != NULL);
280         g_return_if_fail (name != NULL || name [0] != '\0');
281
282         soup_message_set_header (&req->response_headers, name, value);
283 }
284
285 /**
286  * soup_message_get_response_header:
287  * @req: a %SoupMessage.
288  * @name: header name.
289  * 
290  * Lookup the transport response header with a key equal to @name.
291  *
292  * Return value: the header's value or NULL if not found.
293  */
294 const gchar *
295 soup_message_get_response_header (SoupMessage *req,
296                                   const gchar *name) 
297 {
298         g_return_val_if_fail (req != NULL, NULL);
299         g_return_val_if_fail (name != NULL || name [0] != '\0', NULL);
300
301         return req->response_headers ? 
302                 g_hash_table_lookup (req->response_headers, name) : NULL;
303 }
304
305 /**
306  * soup_message_send:
307  * @msg: a %SoupMessage.
308  * 
309  * Syncronously send @msg. This call will not return until the transfer is
310  * finished successfully or there is an unrecoverable error. 
311  *
312  * @msg is not free'd upon return.
313  *
314  * Return value: the %SoupErrorCode of the error encountered while sending, or
315  * SOUP_ERROR_NONE.
316  */
317 SoupErrorCode 
318 soup_message_send (SoupMessage *msg)
319 {
320         soup_message_queue (msg, NULL, NULL);
321
322         while (1) {
323                 g_main_iteration (TRUE); 
324                 if (msg->status == SOUP_STATUS_FINISHED ||
325                     msg->priv->errorcode != SOUP_ERROR_NONE)
326                         return msg->priv->errorcode;
327         }
328
329         return SOUP_ERROR_NONE;
330 }
331
332 void
333 soup_message_set_flags (SoupMessage *msg, guint flags)
334 {
335         msg->priv->msg_flags = flags;
336 }
337
338 guint
339 soup_message_get_flags (SoupMessage *msg)
340 {
341         return msg->priv->msg_flags;
342 }