1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * soup-message.c: HTTP request/response
5 * Copyright (C) 2000-2003, Ximian, Inc.
10 #include "soup-auth.h"
11 #include "soup-error.h"
12 #include "soup-message.h"
13 #include "soup-message-private.h"
14 #include "soup-misc.h"
15 #include "soup-context.h"
16 #include "soup-private.h"
17 #include "soup-queue.h"
19 #define PARENT_TYPE G_TYPE_OBJECT
20 static GObjectClass *parent_class;
22 static void cleanup_message (SoupMessage *req);
25 init (GObject *object)
27 SoupMessage *msg = SOUP_MESSAGE (object);
29 msg->priv = g_new0 (SoupMessagePrivate, 1);
31 msg->priv->status = SOUP_MESSAGE_STATUS_IDLE;
33 msg->request_headers = g_hash_table_new (soup_str_case_hash,
36 msg->response_headers = g_hash_table_new (soup_str_case_hash,
39 msg->priv->http_version = SOUP_HTTP_1_1;
43 finalize (GObject *object)
45 SoupMessage *msg = SOUP_MESSAGE (object);
47 cleanup_message (msg);
49 if (msg->priv->context)
50 g_object_unref (msg->priv->context);
52 if (msg->request.owner == SOUP_BUFFER_SYSTEM_OWNED)
53 g_free (msg->request.body);
54 if (msg->response.owner == SOUP_BUFFER_SYSTEM_OWNED)
55 g_free (msg->response.body);
57 soup_message_clear_headers (msg->request_headers);
58 g_hash_table_destroy (msg->request_headers);
60 soup_message_clear_headers (msg->response_headers);
61 g_hash_table_destroy (msg->response_headers);
63 g_slist_foreach (msg->priv->content_handlers, (GFunc) g_free, NULL);
64 g_slist_free (msg->priv->content_handlers);
66 g_free ((char *) msg->errorphrase);
70 G_OBJECT_CLASS (parent_class)->finalize (object);
74 class_init (GObjectClass *object_class)
76 parent_class = g_type_class_ref (PARENT_TYPE);
78 /* virtual method override */
79 object_class->finalize = finalize;
82 SOUP_MAKE_TYPE (soup_message, SoupMessage, class_init, init, PARENT_TYPE)
87 * @method: the HTTP method for the created request
88 * @uri: the destination endpoint (as a string)
90 * Creates a new empty #SoupMessage, which will connect to @uri
92 * Return value: the new #SoupMessage (or %NULL if @uri could not
96 soup_message_new (const char *method, const char *uri)
101 ctx = soup_context_get (uri);
105 msg = g_object_new (SOUP_TYPE_MESSAGE, NULL);
106 msg->method = method ? method : SOUP_METHOD_GET;
107 msg->priv->context = ctx;
113 * soup_message_new_from_uri:
114 * @method: the HTTP method for the created request
115 * @uri: the destination endpoint (as a #SoupUri)
117 * Creates a new empty #SoupMessage, which will connect to @uri
119 * Return value: the new #SoupMessage (or %NULL if @uri is invalid)
122 soup_message_new_from_uri (const char *method, const SoupUri *uri)
127 ctx = soup_context_from_uri (uri);
131 msg = g_object_new (SOUP_TYPE_MESSAGE, NULL);
132 msg->method = method ? method : SOUP_METHOD_GET;
133 msg->priv->context = ctx;
139 * soup_message_set_request:
141 * @content_type: MIME Content-Type of the body
142 * @req_owner: the #SoupOwnership of the passed data buffer.
143 * @req_body: a data buffer containing the body of the message request.
144 * @req_length: the byte length of @req_body.
146 * Convenience function to set the request body of a #SoupMessage
149 soup_message_set_request (SoupMessage *msg,
150 const char *content_type,
151 SoupOwnership req_owner,
155 g_return_if_fail (SOUP_IS_MESSAGE (msg));
156 g_return_if_fail (content_type != NULL);
157 g_return_if_fail (req_body != NULL || req_length == 0);
159 soup_message_add_header (msg->request_headers,
160 "Content-Type", content_type);
161 msg->request.owner = req_owner;
162 msg->request.body = req_body;
163 msg->request.length = req_length;
167 * soup_message_set_response:
169 * @content_type: MIME Content-Type of the body
170 * @req_owner: the #SoupOwnership of the passed data buffer.
171 * @req_body: a data buffer containing the body of the message response.
172 * @req_length: the byte length of @req_body.
174 * Convenience function to set the response body of a #SoupMessage
177 soup_message_set_response (SoupMessage *msg,
178 const char *content_type,
179 SoupOwnership resp_owner,
183 g_return_if_fail (SOUP_IS_MESSAGE (msg));
184 g_return_if_fail (content_type != NULL);
185 g_return_if_fail (resp_body != NULL || resp_length == 0);
187 soup_message_add_header (msg->response_headers,
188 "Content-Type", content_type);
189 msg->response.owner = resp_owner;
190 msg->response.body = resp_body;
191 msg->response.length = resp_length;
195 cleanup_message (SoupMessage *req)
197 if (req->priv->read_state)
198 soup_message_read_cancel (req);
200 if (req->priv->write_state)
201 soup_message_write_cancel (req);
203 if (req->priv->connect_tag) {
204 soup_context_cancel_connect (req->priv->connect_tag);
205 req->priv->connect_tag = NULL;
208 soup_message_set_connection (req, NULL);
210 soup_queue_remove_request (req);
214 * soup_message_issue_callback:
215 * @req: a #SoupMessage currently being processed.
216 * @error: a #SoupErrorCode to be passed to @req's completion callback.
218 * Finalizes the message request, by first freeing any temporary
219 * resources, then issuing the callback function pointer passed in
220 * soup_message_new() or soup_message_new_full(). If, after returning
221 * from the callback, the message has not been requeued, @req will be
225 soup_message_issue_callback (SoupMessage *req)
227 g_return_if_fail (SOUP_IS_MESSAGE (req));
230 * Make sure we don't have some icky recursion if the callback
231 * runs the main loop, and the connection has some data or error
232 * which causes the callback to be run again.
234 cleanup_message (req);
236 if (req->priv->callback) {
237 (*req->priv->callback) (req, req->priv->user_data);
239 if (!SOUP_MESSAGE_IS_STARTING (req))
240 g_object_unref (req);
245 * soup_message_disconnect:
246 * @msg: a #SoupMessage
248 * Utility function to close and unref the connection associated with
249 * @msg if there was an error.
252 soup_message_disconnect (SoupMessage *msg)
254 if (msg->priv->connection) {
255 soup_connection_disconnect (msg->priv->connection);
256 soup_message_set_connection (msg, NULL);
261 * soup_message_cancel:
262 * @msg: a #SoupMessage currently being processed.
264 * Cancel a running message, and issue completion callback with an
265 * error code of %SOUP_ERROR_CANCELLED. If not requeued by the
266 * completion callback, the @msg will be destroyed.
269 soup_message_cancel (SoupMessage *msg)
271 soup_message_set_error (msg, SOUP_ERROR_CANCELLED);
272 soup_message_disconnect (msg);
273 soup_message_issue_callback (msg);
277 free_header_list (gpointer name, gpointer vals, gpointer user_data)
280 g_slist_foreach (vals, (GFunc) g_free, NULL);
287 soup_message_clear_headers (GHashTable *hash)
289 g_return_if_fail (hash != NULL);
291 g_hash_table_foreach_remove (hash, free_header_list, NULL);
295 soup_message_remove_header (GHashTable *hash, const char *name)
297 gpointer old_key, old_vals;
299 g_return_if_fail (hash != NULL);
300 g_return_if_fail (name != NULL || name[0] != '\0');
302 if (g_hash_table_lookup_extended (hash, name, &old_key, &old_vals)) {
303 g_hash_table_remove (hash, name);
304 free_header_list (old_key, old_vals, NULL);
309 soup_message_add_header (GHashTable *hash, const char *name, const char *value)
313 g_return_if_fail (hash != NULL);
314 g_return_if_fail (name != NULL || name [0] != '\0');
315 g_return_if_fail (value != NULL);
317 old_value = g_hash_table_lookup (hash, name);
320 g_slist_append (old_value, g_strdup (value));
322 g_hash_table_insert (hash, g_strdup (name),
323 g_slist_append (NULL, g_strdup (value)));
328 * soup_message_get_header:
329 * @hash: a header hash table
330 * @name: header name.
332 * Lookup the first transport header in @hash with a key equal to
335 * Return value: the header's value or %NULL if not found.
338 soup_message_get_header (GHashTable *hash, const char *name)
342 g_return_val_if_fail (hash != NULL, NULL);
343 g_return_val_if_fail (name != NULL || name [0] != '\0', NULL);
345 vals = g_hash_table_lookup (hash, name);
353 * soup_message_get_header_list:
354 * @hash: a header hash table
355 * @name: header name.
357 * Lookup the all transport request headers in @hash with a key equal
360 * Return value: a const pointer to a #GSList of header values or
361 * %NULL if not found.
364 soup_message_get_header_list (GHashTable *hash, const char *name)
366 g_return_val_if_fail (hash != NULL, NULL);
367 g_return_val_if_fail (name != NULL || name [0] != '\0', NULL);
369 return g_hash_table_lookup (hash, name);
375 } SoupMessageForeachHeaderData;
378 foreach_value_in_list (gpointer name, gpointer value, gpointer user_data)
380 GSList *vals = value;
381 SoupMessageForeachHeaderData *data = user_data;
384 (*data->func) (name, vals->data, data->user_data);
390 soup_message_foreach_header (GHashTable *hash, GHFunc func, gpointer user_data)
392 SoupMessageForeachHeaderData data;
394 g_return_if_fail (hash != NULL);
395 g_return_if_fail (func != NULL);
398 data.user_data = user_data;
399 g_hash_table_foreach (hash, foreach_value_in_list, &data);
403 queue_message (SoupMessage *req)
405 if (!req->priv->context) {
406 soup_message_set_error_full (req,
407 SOUP_ERROR_CANCELLED,
408 "Attempted to queue a message "
409 "with no destination context");
410 soup_message_issue_callback (req);
414 if (req->priv->status != SOUP_MESSAGE_STATUS_IDLE)
415 cleanup_message (req);
417 switch (req->response.owner) {
418 case SOUP_BUFFER_USER_OWNED:
419 soup_message_set_error_full (req,
420 SOUP_ERROR_CANCELLED,
421 "Attempted to queue a message "
422 "with a user owned response "
424 soup_message_issue_callback (req);
426 case SOUP_BUFFER_SYSTEM_OWNED:
427 g_free (req->response.body);
429 case SOUP_BUFFER_STATIC:
433 req->response.owner = 0;
434 req->response.body = NULL;
435 req->response.length = 0;
437 soup_message_clear_headers (req->response_headers);
442 if (req->errorphrase) {
443 g_free ((char *) req->errorphrase);
444 req->errorphrase = NULL;
447 soup_queue_message (req);
451 * soup_message_queue:
452 * @req: a #SoupMessage.
453 * @callback: a #SoupCallbackFn which will be called after the message
454 * completes or when an unrecoverable error occurs.
455 * @user_data: a pointer passed to @callback.
457 * Queues the message @req for sending. All messages are processed
458 * while the glib main loop runs. If this #SoupMessage has been
459 * processed before, any resources related to the time it was last
462 * If the response #SoupDataBuffer has an owner of
463 * %SOUP_BUFFER_USER_OWNED, the message will not be queued, and
464 * @callback will be called with a #SoupErrorCode of
465 * %SOUP_ERROR_CANCELLED.
467 * Upon message completetion, the callback specified in @callback will
468 * be invoked. If after returning from this callback the message has
469 * not been requeued using soup_message_queue(), @req will be unreffed.
472 soup_message_queue (SoupMessage *req,
473 SoupCallbackFn callback,
476 g_return_if_fail (SOUP_IS_MESSAGE (req));
478 req->priv->callback = callback;
479 req->priv->user_data = user_data;
485 requeue_read_error (SoupMessage *msg)
487 soup_message_disconnect (msg);
492 requeue_read_finished (SoupMessage *msg, char *body, guint len)
494 SoupConnection *conn = msg->priv->connection;
499 soup_message_set_connection (msg, NULL);
501 if (soup_connection_is_connected (conn)) {
502 soup_connection_mark_old (conn);
504 g_object_unref (conn);
509 soup_message_set_connection (msg, conn);
513 * soup_message_requeue:
514 * @req: a #SoupMessage
516 * This causes @req to be placed back on the queue to be attempted
520 soup_message_requeue (SoupMessage *req)
522 g_return_if_fail (SOUP_IS_MESSAGE (req));
524 if (req->priv->connection && req->priv->read_state) {
525 soup_message_read_set_callbacks (req, NULL, NULL,
526 requeue_read_finished,
529 if (req->priv->write_state)
530 soup_message_write_cancel (req);
537 * @msg: a #SoupMessage.
539 * Synchronously send @msg. This call will not return until the
540 * transfer is finished successfully or there is an unrecoverable
543 * @msg is not freed upon return.
545 * Return value: the #SoupErrorClass of the error encountered while
546 * sending or reading the response.
549 soup_message_send (SoupMessage *msg)
551 soup_message_queue (msg, NULL, NULL);
554 g_main_iteration (TRUE);
556 if (msg->priv->status == SOUP_MESSAGE_STATUS_FINISHED ||
557 SOUP_ERROR_IS_TRANSPORT (msg->errorcode))
560 /* Quit if soup_shutdown has been called */
561 if (!soup_initialized)
562 return SOUP_ERROR_CANCELLED;
565 return msg->errorclass;
569 soup_message_set_flags (SoupMessage *msg, guint flags)
571 g_return_if_fail (SOUP_IS_MESSAGE (msg));
573 msg->priv->msg_flags = flags;
577 soup_message_get_flags (SoupMessage *msg)
579 g_return_val_if_fail (SOUP_IS_MESSAGE (msg), 0);
581 return msg->priv->msg_flags;
585 soup_message_set_http_version (SoupMessage *msg, SoupHttpVersion version)
587 g_return_if_fail (SOUP_IS_MESSAGE (msg));
589 msg->priv->http_version = version;
593 soup_message_get_http_version (SoupMessage *msg)
595 g_return_val_if_fail (SOUP_IS_MESSAGE (msg), SOUP_HTTP_1_0);
597 return msg->priv->http_version;
601 soup_message_is_keepalive (SoupMessage *msg)
603 const char *c_conn, *s_conn;
605 c_conn = soup_message_get_header (msg->request_headers, "Connection");
606 s_conn = soup_message_get_header (msg->response_headers, "Connection");
608 if (msg->priv->http_version == SOUP_HTTP_1_0) {
609 /* Only persistent if the client requested keepalive
610 * and the server agreed.
613 if (!c_conn || !s_conn)
615 if (g_strcasecmp (c_conn, "Keep-Alive") != 0 ||
616 g_strcasecmp (s_conn, "Keep-Alive") != 0)
621 /* Persistent unless either side requested otherwise */
623 if (c_conn && g_strcasecmp (c_conn, "close") == 0)
625 if (s_conn && g_strcasecmp (s_conn, "close") == 0)
633 soup_message_set_context (SoupMessage *msg, SoupContext *new_ctx)
635 g_return_if_fail (SOUP_IS_MESSAGE (msg));
637 if (msg->priv->context && new_ctx) {
638 const SoupUri *old, *new;
640 old = soup_context_get_uri (msg->priv->context);
641 new = soup_context_get_uri (new_ctx);
642 if (strcmp (old->host, new->host) != 0)
643 cleanup_message (msg);
645 cleanup_message (msg);
648 g_object_ref (new_ctx);
649 if (msg->priv->context)
650 g_object_unref (msg->priv->context);
652 msg->priv->context = new_ctx;
656 soup_message_get_uri (SoupMessage *msg)
658 g_return_val_if_fail (SOUP_IS_MESSAGE (msg), NULL);
660 return soup_context_get_uri (msg->priv->context);
664 soup_message_set_connection (SoupMessage *msg, SoupConnection *conn)
667 soup_connection_set_in_use (conn, TRUE);
670 if (msg->priv->connection) {
671 soup_connection_set_in_use (msg->priv->connection, FALSE);
672 g_object_unref (msg->priv->connection);
675 msg->priv->connection = conn;
678 msg->priv->socket = soup_connection_get_socket (conn);
679 g_object_ref (msg->priv->socket);
680 } else if (msg->priv->socket) {
681 g_object_unref (msg->priv->socket);
682 msg->priv->socket = NULL;
687 soup_message_get_connection (SoupMessage *msg)
689 g_return_val_if_fail (SOUP_IS_MESSAGE (msg), NULL);
691 return msg->priv->connection;
695 soup_message_get_socket (SoupMessage *msg)
697 g_return_val_if_fail (SOUP_IS_MESSAGE (msg), NULL);
699 return msg->priv->socket;
703 soup_message_set_error (SoupMessage *msg, SoupKnownErrorCode errcode)
705 g_return_if_fail (SOUP_IS_MESSAGE (msg));
706 g_return_if_fail (errcode != 0);
708 g_free ((char *) msg->errorphrase);
710 msg->errorcode = errcode;
711 msg->errorclass = soup_error_get_class (errcode);
712 msg->errorphrase = g_strdup (soup_error_get_phrase (errcode));
716 soup_message_set_error_full (SoupMessage *msg,
718 const char *errphrase)
720 g_return_if_fail (SOUP_IS_MESSAGE (msg));
721 g_return_if_fail (errcode != 0);
722 g_return_if_fail (errphrase != NULL);
724 g_free ((char *) msg->errorphrase);
726 msg->errorcode = errcode;
727 msg->errorclass = soup_error_get_class (errcode);
728 msg->errorphrase = g_strdup (errphrase);