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-marshal.h"
12 #include "soup-message.h"
13 #include "soup-message-private.h"
14 #include "soup-misc.h"
17 #define PARENT_TYPE G_TYPE_OBJECT
18 static GObjectClass *parent_class;
36 static 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 free_chunks (SoupMessage *msg);
45 init (GObject *object)
47 SoupMessage *msg = SOUP_MESSAGE (object);
49 msg->priv = g_new0 (SoupMessagePrivate, 1);
51 msg->status = SOUP_MESSAGE_STATUS_IDLE;
53 msg->request_headers = g_hash_table_new (soup_str_case_hash,
56 msg->response_headers = g_hash_table_new (soup_str_case_hash,
59 msg->priv->http_version = SOUP_HTTP_1_1;
63 finalize (GObject *object)
65 SoupMessage *msg = SOUP_MESSAGE (object);
67 soup_message_io_cancel (msg);
70 soup_uri_free (msg->priv->uri);
72 if (msg->request.owner == SOUP_BUFFER_SYSTEM_OWNED)
73 g_free (msg->request.body);
74 if (msg->response.owner == SOUP_BUFFER_SYSTEM_OWNED)
75 g_free (msg->response.body);
78 soup_message_clear_headers (msg->request_headers);
79 g_hash_table_destroy (msg->request_headers);
81 soup_message_clear_headers (msg->response_headers);
82 g_hash_table_destroy (msg->response_headers);
84 g_slist_foreach (msg->priv->content_handlers, (GFunc) g_free, NULL);
85 g_slist_free (msg->priv->content_handlers);
87 g_free ((char *) msg->reason_phrase);
91 G_OBJECT_CLASS (parent_class)->finalize (object);
95 class_init (GObjectClass *object_class)
97 SoupMessageClass *message_class = SOUP_MESSAGE_CLASS (object_class);
99 parent_class = g_type_class_ref (PARENT_TYPE);
101 /* virtual method definition */
102 message_class->got_headers = got_headers;
103 message_class->got_chunk = got_chunk;
104 message_class->got_body = got_body;
105 message_class->finished = finished;
107 /* virtual method override */
108 object_class->finalize = finalize;
111 signals[WROTE_INFORMATIONAL] =
112 g_signal_new ("wrote_informational",
113 G_OBJECT_CLASS_TYPE (object_class),
115 G_STRUCT_OFFSET (SoupMessageClass, wrote_informational),
117 soup_marshal_NONE__NONE,
119 signals[WROTE_HEADERS] =
120 g_signal_new ("wrote_headers",
121 G_OBJECT_CLASS_TYPE (object_class),
123 G_STRUCT_OFFSET (SoupMessageClass, wrote_headers),
125 soup_marshal_NONE__NONE,
127 signals[WROTE_CHUNK] =
128 g_signal_new ("wrote_chunk",
129 G_OBJECT_CLASS_TYPE (object_class),
131 G_STRUCT_OFFSET (SoupMessageClass, wrote_chunk),
133 soup_marshal_NONE__NONE,
135 signals[WROTE_BODY] =
136 g_signal_new ("wrote_body",
137 G_OBJECT_CLASS_TYPE (object_class),
139 G_STRUCT_OFFSET (SoupMessageClass, wrote_body),
141 soup_marshal_NONE__NONE,
144 signals[GOT_INFORMATIONAL] =
145 g_signal_new ("got_informational",
146 G_OBJECT_CLASS_TYPE (object_class),
148 G_STRUCT_OFFSET (SoupMessageClass, got_informational),
150 soup_marshal_NONE__NONE,
152 signals[GOT_HEADERS] =
153 g_signal_new ("got_headers",
154 G_OBJECT_CLASS_TYPE (object_class),
156 G_STRUCT_OFFSET (SoupMessageClass, got_headers),
158 soup_marshal_NONE__NONE,
161 g_signal_new ("got_chunk",
162 G_OBJECT_CLASS_TYPE (object_class),
164 G_STRUCT_OFFSET (SoupMessageClass, got_chunk),
166 soup_marshal_NONE__NONE,
169 g_signal_new ("got_body",
170 G_OBJECT_CLASS_TYPE (object_class),
172 G_STRUCT_OFFSET (SoupMessageClass, got_body),
174 soup_marshal_NONE__NONE,
178 g_signal_new ("finished",
179 G_OBJECT_CLASS_TYPE (object_class),
181 G_STRUCT_OFFSET (SoupMessageClass, finished),
183 soup_marshal_NONE__NONE,
187 SOUP_MAKE_TYPE (soup_message, SoupMessage, class_init, init, PARENT_TYPE)
192 * @method: the HTTP method for the created request
193 * @uri_string: the destination endpoint (as a string)
195 * Creates a new empty #SoupMessage, which will connect to @uri
197 * Return value: the new #SoupMessage (or %NULL if @uri could not
201 soup_message_new (const char *method, const char *uri_string)
206 uri = soup_uri_new (uri_string);
210 msg = g_object_new (SOUP_TYPE_MESSAGE, NULL);
211 msg->method = method ? method : SOUP_METHOD_GET;
212 msg->priv->uri = uri;
218 * soup_message_new_from_uri:
219 * @method: the HTTP method for the created request
220 * @uri: the destination endpoint (as a #SoupUri)
222 * Creates a new empty #SoupMessage, which will connect to @uri
224 * Return value: the new #SoupMessage
227 soup_message_new_from_uri (const char *method, const SoupUri *uri)
231 msg = g_object_new (SOUP_TYPE_MESSAGE, NULL);
232 msg->method = method ? method : SOUP_METHOD_GET;
233 msg->priv->uri = soup_uri_copy (uri);
239 * soup_message_set_request:
241 * @content_type: MIME Content-Type of the body
242 * @req_owner: the #SoupOwnership of the passed data buffer.
243 * @req_body: a data buffer containing the body of the message request.
244 * @req_length: the byte length of @req_body.
246 * Convenience function to set the request body of a #SoupMessage
249 soup_message_set_request (SoupMessage *msg,
250 const char *content_type,
251 SoupOwnership req_owner,
255 g_return_if_fail (SOUP_IS_MESSAGE (msg));
256 g_return_if_fail (content_type != NULL);
257 g_return_if_fail (req_body != NULL || req_length == 0);
259 soup_message_add_header (msg->request_headers,
260 "Content-Type", content_type);
261 msg->request.owner = req_owner;
262 msg->request.body = req_body;
263 msg->request.length = req_length;
267 * soup_message_set_response:
269 * @content_type: MIME Content-Type of the body
270 * @req_owner: the #SoupOwnership of the passed data buffer.
271 * @req_body: a data buffer containing the body of the message response.
272 * @req_length: the byte length of @req_body.
274 * Convenience function to set the response body of a #SoupMessage
277 soup_message_set_response (SoupMessage *msg,
278 const char *content_type,
279 SoupOwnership resp_owner,
283 g_return_if_fail (SOUP_IS_MESSAGE (msg));
284 g_return_if_fail (content_type != NULL);
285 g_return_if_fail (resp_body != NULL || resp_length == 0);
287 soup_message_add_header (msg->response_headers,
288 "Content-Type", content_type);
289 msg->response.owner = resp_owner;
290 msg->response.body = resp_body;
291 msg->response.length = resp_length;
295 soup_message_wrote_informational (SoupMessage *msg)
297 g_signal_emit (msg, signals[WROTE_INFORMATIONAL], 0);
301 soup_message_wrote_headers (SoupMessage *msg)
303 g_signal_emit (msg, signals[WROTE_HEADERS], 0);
307 soup_message_wrote_chunk (SoupMessage *msg)
309 g_signal_emit (msg, signals[WROTE_CHUNK], 0);
313 soup_message_wrote_body (SoupMessage *msg)
315 g_signal_emit (msg, signals[WROTE_BODY], 0);
319 soup_message_got_informational (SoupMessage *msg)
321 g_signal_emit (msg, signals[GOT_INFORMATIONAL], 0);
325 got_headers (SoupMessage *req)
328 soup_message_run_handlers (req, SOUP_HANDLER_PRE_BODY);
329 if (SOUP_MESSAGE_IS_STARTING (req))
330 g_signal_stop_emission (req, signals[GOT_HEADERS], 0);
331 g_object_unref (req);
335 soup_message_got_headers (SoupMessage *msg)
337 g_signal_emit (msg, signals[GOT_HEADERS], 0);
341 got_chunk (SoupMessage *req)
344 soup_message_run_handlers (req, SOUP_HANDLER_BODY_CHUNK);
345 if (SOUP_MESSAGE_IS_STARTING (req))
346 g_signal_stop_emission (req, signals[GOT_CHUNK], 0);
347 g_object_unref (req);
351 soup_message_got_chunk (SoupMessage *msg)
353 g_signal_emit (msg, signals[GOT_CHUNK], 0);
357 got_body (SoupMessage *req)
360 soup_message_run_handlers (req, SOUP_HANDLER_POST_BODY);
361 if (SOUP_MESSAGE_IS_STARTING (req))
362 g_signal_stop_emission (req, signals[GOT_BODY], 0);
363 g_object_unref (req);
367 soup_message_got_body (SoupMessage *msg)
369 g_signal_emit (msg, signals[GOT_BODY], 0);
373 finished (SoupMessage *req)
375 soup_message_io_cancel (req);
379 soup_message_finished (SoupMessage *msg)
381 g_signal_emit (msg, signals[FINISHED], 0);
386 * soup_message_cancel:
387 * @msg: a #SoupMessage currently being processed.
389 * Cancel a running message, and issue completion callback with an
390 * status code of %SOUP_STATUS_CANCELLED. If not requeued by the
391 * completion callback, the @msg will be destroyed.
394 soup_message_cancel (SoupMessage *msg)
396 soup_message_set_status (msg, SOUP_STATUS_CANCELLED);
397 soup_message_finished (msg);
401 free_header_list (gpointer name, gpointer vals, gpointer user_data)
404 g_slist_foreach (vals, (GFunc) g_free, NULL);
411 soup_message_clear_headers (GHashTable *hash)
413 g_return_if_fail (hash != NULL);
415 g_hash_table_foreach_remove (hash, free_header_list, NULL);
419 soup_message_remove_header (GHashTable *hash, const char *name)
421 gpointer old_key, old_vals;
423 g_return_if_fail (hash != NULL);
424 g_return_if_fail (name != NULL || name[0] != '\0');
426 if (g_hash_table_lookup_extended (hash, name, &old_key, &old_vals)) {
427 g_hash_table_remove (hash, name);
428 free_header_list (old_key, old_vals, NULL);
433 soup_message_add_header (GHashTable *hash, const char *name, const char *value)
437 g_return_if_fail (hash != NULL);
438 g_return_if_fail (name != NULL || name [0] != '\0');
439 g_return_if_fail (value != NULL);
441 old_value = g_hash_table_lookup (hash, name);
444 g_slist_append (old_value, g_strdup (value));
446 g_hash_table_insert (hash, g_strdup (name),
447 g_slist_append (NULL, g_strdup (value)));
452 * soup_message_get_header:
453 * @hash: a header hash table
454 * @name: header name.
456 * Lookup the first transport header in @hash with a key equal to
459 * Return value: the header's value or %NULL if not found.
462 soup_message_get_header (GHashTable *hash, const char *name)
466 g_return_val_if_fail (hash != NULL, NULL);
467 g_return_val_if_fail (name != NULL || name [0] != '\0', NULL);
469 vals = g_hash_table_lookup (hash, name);
477 * soup_message_get_header_list:
478 * @hash: a header hash table
479 * @name: header name.
481 * Lookup the all transport request headers in @hash with a key equal
484 * Return value: a const pointer to a #GSList of header values or
485 * %NULL if not found.
488 soup_message_get_header_list (GHashTable *hash, const char *name)
490 g_return_val_if_fail (hash != NULL, NULL);
491 g_return_val_if_fail (name != NULL || name [0] != '\0', NULL);
493 return g_hash_table_lookup (hash, name);
499 } SoupMessageForeachHeaderData;
502 foreach_value_in_list (gpointer name, gpointer value, gpointer user_data)
504 GSList *vals = value;
505 SoupMessageForeachHeaderData *data = user_data;
508 (*data->func) (name, vals->data, data->user_data);
514 soup_message_foreach_header (GHashTable *hash, GHFunc func, gpointer user_data)
516 SoupMessageForeachHeaderData data;
518 g_return_if_fail (hash != NULL);
519 g_return_if_fail (func != NULL);
522 data.user_data = user_data;
523 g_hash_table_foreach (hash, foreach_value_in_list, &data);
527 soup_message_cleanup_response (SoupMessage *req)
529 if (req->response.owner == SOUP_BUFFER_SYSTEM_OWNED)
530 g_free (req->response.body);
532 req->response.owner = 0;
533 req->response.body = NULL;
534 req->response.length = 0;
538 soup_message_clear_headers (req->response_headers);
540 req->status_code = 0;
541 if (req->reason_phrase) {
542 g_free ((char *) req->reason_phrase);
543 req->reason_phrase = NULL;
548 soup_message_set_flags (SoupMessage *msg, guint flags)
550 g_return_if_fail (SOUP_IS_MESSAGE (msg));
552 msg->priv->msg_flags = flags;
556 soup_message_get_flags (SoupMessage *msg)
558 g_return_val_if_fail (SOUP_IS_MESSAGE (msg), 0);
560 return msg->priv->msg_flags;
564 soup_message_set_http_version (SoupMessage *msg, SoupHttpVersion version)
566 g_return_if_fail (SOUP_IS_MESSAGE (msg));
568 msg->priv->http_version = version;
572 soup_message_get_http_version (SoupMessage *msg)
574 g_return_val_if_fail (SOUP_IS_MESSAGE (msg), SOUP_HTTP_1_0);
576 return msg->priv->http_version;
580 soup_message_is_keepalive (SoupMessage *msg)
582 const char *c_conn, *s_conn;
584 c_conn = soup_message_get_header (msg->request_headers, "Connection");
585 s_conn = soup_message_get_header (msg->response_headers, "Connection");
587 if (msg->priv->http_version == SOUP_HTTP_1_0) {
588 /* Only persistent if the client requested keepalive
589 * and the server agreed.
592 if (!c_conn || !s_conn)
594 if (g_strcasecmp (c_conn, "Keep-Alive") != 0 ||
595 g_strcasecmp (s_conn, "Keep-Alive") != 0)
600 /* Persistent unless either side requested otherwise */
602 if (c_conn && g_strcasecmp (c_conn, "close") == 0)
604 if (s_conn && g_strcasecmp (s_conn, "close") == 0)
612 soup_message_set_uri (SoupMessage *msg, const SoupUri *new_uri)
614 g_return_if_fail (SOUP_IS_MESSAGE (msg));
616 if (msg->priv->uri && new_uri) {
617 if (strcmp (msg->priv->uri->host, new_uri->host) != 0)
618 soup_message_io_cancel (msg);
620 soup_message_io_cancel (msg);
623 soup_uri_free (msg->priv->uri);
624 msg->priv->uri = soup_uri_copy (new_uri);
628 soup_message_get_uri (SoupMessage *msg)
630 g_return_val_if_fail (SOUP_IS_MESSAGE (msg), NULL);
632 return msg->priv->uri;
636 soup_message_set_status (SoupMessage *msg, guint status_code)
638 g_return_if_fail (SOUP_IS_MESSAGE (msg));
639 g_return_if_fail (status_code != 0);
641 g_free ((char *) msg->reason_phrase);
643 msg->status_code = status_code;
644 msg->reason_phrase = g_strdup (soup_status_get_phrase (status_code));
648 soup_message_set_status_full (SoupMessage *msg,
650 const char *reason_phrase)
652 g_return_if_fail (SOUP_IS_MESSAGE (msg));
653 g_return_if_fail (status_code != 0);
654 g_return_if_fail (reason_phrase != NULL);
656 g_free ((char *) msg->reason_phrase);
658 msg->status_code = status_code;
659 msg->reason_phrase = g_strdup (reason_phrase);
664 soup_message_add_chunk (SoupMessage *msg,
669 SoupDataBuffer *chunk;
671 g_return_if_fail (SOUP_IS_MESSAGE (msg));
672 g_return_if_fail (body != NULL || length == 0);
674 chunk = g_new0 (SoupDataBuffer, 1);
675 if (owner == SOUP_BUFFER_USER_OWNED) {
676 chunk->owner = SOUP_BUFFER_SYSTEM_OWNED;
677 chunk->body = g_memdup (body, length);
679 chunk->owner = owner;
680 chunk->body = (char *)body;
682 chunk->length = length;
684 if (msg->priv->chunks) {
685 g_slist_append (msg->priv->last_chunk, chunk);
686 msg->priv->last_chunk = msg->priv->last_chunk->next;
688 msg->priv->chunks = msg->priv->last_chunk =
689 g_slist_append (NULL, chunk);
694 soup_message_add_final_chunk (SoupMessage *msg)
696 soup_message_add_chunk (msg, SOUP_BUFFER_STATIC, NULL, 0);
700 soup_message_pop_chunk (SoupMessage *msg)
702 SoupDataBuffer *chunk;
704 g_return_val_if_fail (SOUP_IS_MESSAGE (msg), NULL);
706 if (!msg->priv->chunks)
709 chunk = msg->priv->chunks->data;
710 msg->priv->chunks = g_slist_remove (msg->priv->chunks, chunk);
711 if (!msg->priv->chunks)
712 msg->priv->last_chunk = NULL;
718 free_chunks (SoupMessage *msg)
720 SoupDataBuffer *chunk;
723 for (ch = msg->priv->chunks; ch; ch = ch->next) {
726 if (chunk->owner == SOUP_BUFFER_SYSTEM_OWNED)
727 g_free (chunk->body);
731 g_slist_free (msg->priv->chunks);
732 msg->priv->chunks = msg->priv->last_chunk = NULL;