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-connection.h"
12 #include "soup-marshal.h"
13 #include "soup-message.h"
14 #include "soup-message-private.h"
15 #include "soup-misc.h"
16 #include "soup-context.h"
17 #include "soup-private.h"
19 #define PARENT_TYPE G_TYPE_OBJECT
20 static GObjectClass *parent_class;
36 guint signals[LAST_SIGNAL] = { 0 };
38 static void got_headers (SoupMessage *req);
39 static void got_chunk (SoupMessage *req);
40 static void got_body (SoupMessage *req);
41 static void finished (SoupMessage *req);
42 static void cleanup_message (SoupMessage *req);
43 static void free_chunks (SoupMessage *msg);
46 init (GObject *object)
48 SoupMessage *msg = SOUP_MESSAGE (object);
50 msg->priv = g_new0 (SoupMessagePrivate, 1);
52 msg->priv->status = SOUP_MESSAGE_STATUS_IDLE;
54 msg->request_headers = g_hash_table_new (soup_str_case_hash,
57 msg->response_headers = g_hash_table_new (soup_str_case_hash,
60 msg->priv->http_version = SOUP_HTTP_1_1;
64 finalize (GObject *object)
66 SoupMessage *msg = SOUP_MESSAGE (object);
68 cleanup_message (msg);
70 if (msg->priv->context)
71 g_object_unref (msg->priv->context);
73 if (msg->request.owner == SOUP_BUFFER_SYSTEM_OWNED)
74 g_free (msg->request.body);
75 if (msg->response.owner == SOUP_BUFFER_SYSTEM_OWNED)
76 g_free (msg->response.body);
79 soup_message_clear_headers (msg->request_headers);
80 g_hash_table_destroy (msg->request_headers);
82 soup_message_clear_headers (msg->response_headers);
83 g_hash_table_destroy (msg->response_headers);
85 g_slist_foreach (msg->priv->content_handlers, (GFunc) g_free, NULL);
86 g_slist_free (msg->priv->content_handlers);
88 g_free ((char *) msg->reason_phrase);
92 G_OBJECT_CLASS (parent_class)->finalize (object);
96 class_init (GObjectClass *object_class)
98 SoupMessageClass *message_class = SOUP_MESSAGE_CLASS (object_class);
100 parent_class = g_type_class_ref (PARENT_TYPE);
102 /* virtual method definition */
103 message_class->got_headers = got_headers;
104 message_class->got_chunk = got_chunk;
105 message_class->got_body = got_body;
106 message_class->finished = finished;
108 /* virtual method override */
109 object_class->finalize = finalize;
112 signals[WROTE_HEADERS] =
113 g_signal_new ("wrote_headers",
114 G_OBJECT_CLASS_TYPE (object_class),
116 G_STRUCT_OFFSET (SoupMessageClass, wrote_headers),
118 soup_marshal_NONE__NONE,
120 signals[WROTE_CHUNK] =
121 g_signal_new ("wrote_chunk",
122 G_OBJECT_CLASS_TYPE (object_class),
124 G_STRUCT_OFFSET (SoupMessageClass, wrote_chunk),
126 soup_marshal_NONE__NONE,
128 signals[WROTE_BODY] =
129 g_signal_new ("wrote_body",
130 G_OBJECT_CLASS_TYPE (object_class),
132 G_STRUCT_OFFSET (SoupMessageClass, wrote_body),
134 soup_marshal_NONE__NONE,
137 signals[GOT_HEADERS] =
138 g_signal_new ("got_headers",
139 G_OBJECT_CLASS_TYPE (object_class),
141 G_STRUCT_OFFSET (SoupMessageClass, got_headers),
143 soup_marshal_NONE__NONE,
146 g_signal_new ("got_chunk",
147 G_OBJECT_CLASS_TYPE (object_class),
149 G_STRUCT_OFFSET (SoupMessageClass, got_chunk),
151 soup_marshal_NONE__NONE,
154 g_signal_new ("got_body",
155 G_OBJECT_CLASS_TYPE (object_class),
157 G_STRUCT_OFFSET (SoupMessageClass, got_body),
159 soup_marshal_NONE__NONE,
163 g_signal_new ("finished",
164 G_OBJECT_CLASS_TYPE (object_class),
166 G_STRUCT_OFFSET (SoupMessageClass, finished),
168 soup_marshal_NONE__NONE,
172 SOUP_MAKE_TYPE (soup_message, SoupMessage, class_init, init, PARENT_TYPE)
177 * @method: the HTTP method for the created request
178 * @uri: the destination endpoint (as a string)
180 * Creates a new empty #SoupMessage, which will connect to @uri
182 * Return value: the new #SoupMessage (or %NULL if @uri could not
186 soup_message_new (const char *method, const char *uri)
191 ctx = soup_context_get (uri);
195 msg = g_object_new (SOUP_TYPE_MESSAGE, NULL);
196 msg->method = method ? method : SOUP_METHOD_GET;
197 msg->priv->context = ctx;
203 * soup_message_new_from_uri:
204 * @method: the HTTP method for the created request
205 * @uri: the destination endpoint (as a #SoupUri)
207 * Creates a new empty #SoupMessage, which will connect to @uri
209 * Return value: the new #SoupMessage (or %NULL if @uri is invalid)
212 soup_message_new_from_uri (const char *method, const SoupUri *uri)
217 ctx = soup_context_from_uri (uri);
221 msg = g_object_new (SOUP_TYPE_MESSAGE, NULL);
222 msg->method = method ? method : SOUP_METHOD_GET;
223 msg->priv->context = ctx;
229 * soup_message_set_request:
231 * @content_type: MIME Content-Type of the body
232 * @req_owner: the #SoupOwnership of the passed data buffer.
233 * @req_body: a data buffer containing the body of the message request.
234 * @req_length: the byte length of @req_body.
236 * Convenience function to set the request body of a #SoupMessage
239 soup_message_set_request (SoupMessage *msg,
240 const char *content_type,
241 SoupOwnership req_owner,
245 g_return_if_fail (SOUP_IS_MESSAGE (msg));
246 g_return_if_fail (content_type != NULL);
247 g_return_if_fail (req_body != NULL || req_length == 0);
249 soup_message_add_header (msg->request_headers,
250 "Content-Type", content_type);
251 msg->request.owner = req_owner;
252 msg->request.body = req_body;
253 msg->request.length = req_length;
257 * soup_message_set_response:
259 * @content_type: MIME Content-Type of the body
260 * @req_owner: the #SoupOwnership of the passed data buffer.
261 * @req_body: a data buffer containing the body of the message response.
262 * @req_length: the byte length of @req_body.
264 * Convenience function to set the response body of a #SoupMessage
267 soup_message_set_response (SoupMessage *msg,
268 const char *content_type,
269 SoupOwnership resp_owner,
273 g_return_if_fail (SOUP_IS_MESSAGE (msg));
274 g_return_if_fail (content_type != NULL);
275 g_return_if_fail (resp_body != NULL || resp_length == 0);
277 soup_message_add_header (msg->response_headers,
278 "Content-Type", content_type);
279 msg->response.owner = resp_owner;
280 msg->response.body = resp_body;
281 msg->response.length = resp_length;
285 soup_message_wrote_headers (SoupMessage *msg)
287 g_signal_emit (msg, signals[WROTE_HEADERS], 0);
291 soup_message_wrote_chunk (SoupMessage *msg)
293 g_signal_emit (msg, signals[WROTE_CHUNK], 0);
297 soup_message_wrote_body (SoupMessage *msg)
299 g_signal_emit (msg, signals[WROTE_BODY], 0);
303 got_headers (SoupMessage *req)
306 soup_message_run_handlers (req, SOUP_HANDLER_PRE_BODY);
307 if (SOUP_MESSAGE_IS_STARTING (req))
308 g_signal_stop_emission (req, signals[GOT_HEADERS], 0);
309 g_object_unref (req);
313 soup_message_got_headers (SoupMessage *msg)
315 g_signal_emit (msg, signals[GOT_HEADERS], 0);
319 got_chunk (SoupMessage *req)
322 soup_message_run_handlers (req, SOUP_HANDLER_BODY_CHUNK);
323 if (SOUP_MESSAGE_IS_STARTING (req))
324 g_signal_stop_emission (req, signals[GOT_CHUNK], 0);
325 g_object_unref (req);
329 soup_message_got_chunk (SoupMessage *msg)
331 g_signal_emit (msg, signals[GOT_CHUNK], 0);
335 got_body (SoupMessage *req)
338 soup_message_run_handlers (req, SOUP_HANDLER_POST_BODY);
339 if (SOUP_MESSAGE_IS_STARTING (req))
340 g_signal_stop_emission (req, signals[GOT_BODY], 0);
341 g_object_unref (req);
345 soup_message_got_body (SoupMessage *msg)
347 g_signal_emit (msg, signals[GOT_BODY], 0);
351 finished (SoupMessage *req)
353 cleanup_message (req);
357 soup_message_finished (SoupMessage *msg)
359 g_signal_emit (msg, signals[FINISHED], 0);
364 cleanup_message (SoupMessage *req)
366 if (req->priv->io_data)
367 soup_message_io_cancel (req);
369 if (req->priv->connect_tag) {
370 soup_context_cancel_connect (req->priv->connect_tag);
371 req->priv->connect_tag = NULL;
374 soup_message_set_connection (req, NULL);
378 * soup_message_disconnect:
379 * @msg: a #SoupMessage
381 * Utility function to close and unref the connection associated with
382 * @msg if there was an error.
385 soup_message_disconnect (SoupMessage *msg)
387 if (msg->priv->connection) {
388 soup_connection_disconnect (msg->priv->connection);
389 soup_message_set_connection (msg, NULL);
394 * soup_message_cancel:
395 * @msg: a #SoupMessage currently being processed.
397 * Cancel a running message, and issue completion callback with an
398 * error code of %SOUP_STATUS_CANCELLED. If not requeued by the
399 * completion callback, the @msg will be destroyed.
402 soup_message_cancel (SoupMessage *msg)
404 soup_message_set_status (msg, SOUP_STATUS_CANCELLED);
405 soup_message_disconnect (msg);
406 soup_message_finished (msg);
410 free_header_list (gpointer name, gpointer vals, gpointer user_data)
413 g_slist_foreach (vals, (GFunc) g_free, NULL);
420 soup_message_clear_headers (GHashTable *hash)
422 g_return_if_fail (hash != NULL);
424 g_hash_table_foreach_remove (hash, free_header_list, NULL);
428 soup_message_remove_header (GHashTable *hash, const char *name)
430 gpointer old_key, old_vals;
432 g_return_if_fail (hash != NULL);
433 g_return_if_fail (name != NULL || name[0] != '\0');
435 if (g_hash_table_lookup_extended (hash, name, &old_key, &old_vals)) {
436 g_hash_table_remove (hash, name);
437 free_header_list (old_key, old_vals, NULL);
442 soup_message_add_header (GHashTable *hash, const char *name, const char *value)
446 g_return_if_fail (hash != NULL);
447 g_return_if_fail (name != NULL || name [0] != '\0');
448 g_return_if_fail (value != NULL);
450 old_value = g_hash_table_lookup (hash, name);
453 g_slist_append (old_value, g_strdup (value));
455 g_hash_table_insert (hash, g_strdup (name),
456 g_slist_append (NULL, g_strdup (value)));
461 * soup_message_get_header:
462 * @hash: a header hash table
463 * @name: header name.
465 * Lookup the first transport header in @hash with a key equal to
468 * Return value: the header's value or %NULL if not found.
471 soup_message_get_header (GHashTable *hash, const char *name)
475 g_return_val_if_fail (hash != NULL, NULL);
476 g_return_val_if_fail (name != NULL || name [0] != '\0', NULL);
478 vals = g_hash_table_lookup (hash, name);
486 * soup_message_get_header_list:
487 * @hash: a header hash table
488 * @name: header name.
490 * Lookup the all transport request headers in @hash with a key equal
493 * Return value: a const pointer to a #GSList of header values or
494 * %NULL if not found.
497 soup_message_get_header_list (GHashTable *hash, const char *name)
499 g_return_val_if_fail (hash != NULL, NULL);
500 g_return_val_if_fail (name != NULL || name [0] != '\0', NULL);
502 return g_hash_table_lookup (hash, name);
508 } SoupMessageForeachHeaderData;
511 foreach_value_in_list (gpointer name, gpointer value, gpointer user_data)
513 GSList *vals = value;
514 SoupMessageForeachHeaderData *data = user_data;
517 (*data->func) (name, vals->data, data->user_data);
523 soup_message_foreach_header (GHashTable *hash, GHFunc func, gpointer user_data)
525 SoupMessageForeachHeaderData data;
527 g_return_if_fail (hash != NULL);
528 g_return_if_fail (func != NULL);
531 data.user_data = user_data;
532 g_hash_table_foreach (hash, foreach_value_in_list, &data);
536 * soup_message_prepare:
539 * Prepares @req to be sent, by cleaning up its prior response state
542 soup_message_prepare (SoupMessage *req)
544 if (req->priv->status != SOUP_MESSAGE_STATUS_IDLE) {
545 cleanup_message (req);
546 req->priv->status = SOUP_MESSAGE_STATUS_IDLE;
549 if (req->response.owner == SOUP_BUFFER_SYSTEM_OWNED)
550 g_free (req->response.body);
552 req->response.owner = 0;
553 req->response.body = NULL;
554 req->response.length = 0;
558 soup_message_clear_headers (req->response_headers);
560 req->status_code = 0;
561 if (req->reason_phrase) {
562 g_free ((char *) req->reason_phrase);
563 req->reason_phrase = NULL;
568 soup_message_set_flags (SoupMessage *msg, guint flags)
570 g_return_if_fail (SOUP_IS_MESSAGE (msg));
572 msg->priv->msg_flags = flags;
576 soup_message_get_flags (SoupMessage *msg)
578 g_return_val_if_fail (SOUP_IS_MESSAGE (msg), 0);
580 return msg->priv->msg_flags;
584 soup_message_set_http_version (SoupMessage *msg, SoupHttpVersion version)
586 g_return_if_fail (SOUP_IS_MESSAGE (msg));
588 msg->priv->http_version = version;
592 soup_message_get_http_version (SoupMessage *msg)
594 g_return_val_if_fail (SOUP_IS_MESSAGE (msg), SOUP_HTTP_1_0);
596 return msg->priv->http_version;
600 soup_message_is_keepalive (SoupMessage *msg)
602 const char *c_conn, *s_conn;
604 c_conn = soup_message_get_header (msg->request_headers, "Connection");
605 s_conn = soup_message_get_header (msg->response_headers, "Connection");
607 if (msg->priv->http_version == SOUP_HTTP_1_0) {
608 /* Only persistent if the client requested keepalive
609 * and the server agreed.
612 if (!c_conn || !s_conn)
614 if (g_strcasecmp (c_conn, "Keep-Alive") != 0 ||
615 g_strcasecmp (s_conn, "Keep-Alive") != 0)
620 /* Persistent unless either side requested otherwise */
622 if (c_conn && g_strcasecmp (c_conn, "close") == 0)
624 if (s_conn && g_strcasecmp (s_conn, "close") == 0)
632 soup_message_set_context (SoupMessage *msg, SoupContext *new_ctx)
634 g_return_if_fail (SOUP_IS_MESSAGE (msg));
636 if (msg->priv->context && new_ctx) {
637 const SoupUri *old, *new;
639 old = soup_context_get_uri (msg->priv->context);
640 new = soup_context_get_uri (new_ctx);
641 if (strcmp (old->host, new->host) != 0)
642 cleanup_message (msg);
644 cleanup_message (msg);
647 g_object_ref (new_ctx);
648 if (msg->priv->context)
649 g_object_unref (msg->priv->context);
651 msg->priv->context = new_ctx;
655 soup_message_get_uri (SoupMessage *msg)
657 g_return_val_if_fail (SOUP_IS_MESSAGE (msg), NULL);
659 return soup_context_get_uri (msg->priv->context);
663 soup_message_set_connection (SoupMessage *msg, SoupConnection *conn)
667 if (msg->priv->connection)
668 g_object_unref (msg->priv->connection);
670 msg->priv->connection = conn;
674 soup_message_get_connection (SoupMessage *msg)
676 g_return_val_if_fail (SOUP_IS_MESSAGE (msg), NULL);
678 return msg->priv->connection;
682 soup_message_set_status (SoupMessage *msg, guint status_code)
684 g_return_if_fail (SOUP_IS_MESSAGE (msg));
685 g_return_if_fail (status_code != 0);
687 g_free ((char *) msg->reason_phrase);
689 msg->status_code = status_code;
690 msg->reason_phrase = g_strdup (soup_status_get_phrase (status_code));
694 soup_message_set_status_full (SoupMessage *msg,
696 const char *reason_phrase)
698 g_return_if_fail (SOUP_IS_MESSAGE (msg));
699 g_return_if_fail (status_code != 0);
700 g_return_if_fail (reason_phrase != NULL);
702 g_free ((char *) msg->reason_phrase);
704 msg->status_code = status_code;
705 msg->reason_phrase = g_strdup (reason_phrase);
710 soup_message_add_chunk (SoupMessage *msg,
715 SoupDataBuffer *chunk;
717 g_return_if_fail (SOUP_IS_MESSAGE (msg));
718 g_return_if_fail (body != NULL || length == 0);
720 chunk = g_new0 (SoupDataBuffer, 1);
721 if (owner == SOUP_BUFFER_USER_OWNED) {
722 chunk->owner = SOUP_BUFFER_SYSTEM_OWNED;
723 chunk->body = g_memdup (body, length);
725 chunk->owner = owner;
726 chunk->body = (char *)body;
728 chunk->length = length;
730 if (msg->priv->chunks) {
731 g_slist_append (msg->priv->last_chunk, chunk);
732 msg->priv->last_chunk = msg->priv->last_chunk->next;
734 msg->priv->chunks = msg->priv->last_chunk =
735 g_slist_append (NULL, chunk);
740 soup_message_add_final_chunk (SoupMessage *msg)
742 soup_message_add_chunk (msg, SOUP_BUFFER_STATIC, NULL, 0);
746 soup_message_pop_chunk (SoupMessage *msg)
748 SoupDataBuffer *chunk;
750 g_return_val_if_fail (SOUP_IS_MESSAGE (msg), NULL);
752 if (!msg->priv->chunks)
755 chunk = msg->priv->chunks->data;
756 msg->priv->chunks = g_slist_remove (msg->priv->chunks, chunk);
757 if (!msg->priv->chunks)
758 msg->priv->last_chunk = NULL;
764 free_chunks (SoupMessage *msg)
766 SoupDataBuffer *chunk;
769 for (ch = msg->priv->chunks; ch; ch = ch->next) {
772 if (chunk->owner == SOUP_BUFFER_SYSTEM_OWNED)
773 g_free (chunk->body);
777 g_slist_free (msg->priv->chunks);
778 msg->priv->chunks = msg->priv->last_chunk = NULL;