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;
37 static guint signals[LAST_SIGNAL] = { 0 };
39 static void wrote_body (SoupMessage *req);
40 static void got_headers (SoupMessage *req);
41 static void got_chunk (SoupMessage *req);
42 static void got_body (SoupMessage *req);
43 static void restarted (SoupMessage *req);
44 static void finished (SoupMessage *req);
45 static void free_chunks (SoupMessage *msg);
48 init (GObject *object)
50 SoupMessage *msg = SOUP_MESSAGE (object);
52 msg->priv = g_new0 (SoupMessagePrivate, 1);
54 msg->status = SOUP_MESSAGE_STATUS_IDLE;
56 msg->request_headers = g_hash_table_new (soup_str_case_hash,
59 msg->response_headers = g_hash_table_new (soup_str_case_hash,
62 msg->priv->http_version = SOUP_HTTP_1_1;
66 finalize (GObject *object)
68 SoupMessage *msg = SOUP_MESSAGE (object);
70 soup_message_io_cancel (msg);
73 soup_uri_free (msg->priv->uri);
75 if (msg->request.owner == SOUP_BUFFER_SYSTEM_OWNED)
76 g_free (msg->request.body);
77 if (msg->response.owner == SOUP_BUFFER_SYSTEM_OWNED)
78 g_free (msg->response.body);
81 soup_message_clear_headers (msg->request_headers);
82 g_hash_table_destroy (msg->request_headers);
84 soup_message_clear_headers (msg->response_headers);
85 g_hash_table_destroy (msg->response_headers);
87 g_slist_foreach (msg->priv->content_handlers, (GFunc) g_free, NULL);
88 g_slist_free (msg->priv->content_handlers);
90 g_free ((char *) msg->reason_phrase);
94 G_OBJECT_CLASS (parent_class)->finalize (object);
98 class_init (GObjectClass *object_class)
100 SoupMessageClass *message_class = SOUP_MESSAGE_CLASS (object_class);
102 parent_class = g_type_class_ref (PARENT_TYPE);
104 /* virtual method definition */
105 message_class->wrote_body = wrote_body;
106 message_class->got_headers = got_headers;
107 message_class->got_chunk = got_chunk;
108 message_class->got_body = got_body;
109 message_class->restarted = restarted;
110 message_class->finished = finished;
112 /* virtual method override */
113 object_class->finalize = finalize;
116 signals[WROTE_INFORMATIONAL] =
117 g_signal_new ("wrote_informational",
118 G_OBJECT_CLASS_TYPE (object_class),
120 G_STRUCT_OFFSET (SoupMessageClass, wrote_informational),
122 soup_marshal_NONE__NONE,
124 signals[WROTE_HEADERS] =
125 g_signal_new ("wrote_headers",
126 G_OBJECT_CLASS_TYPE (object_class),
128 G_STRUCT_OFFSET (SoupMessageClass, wrote_headers),
130 soup_marshal_NONE__NONE,
132 signals[WROTE_CHUNK] =
133 g_signal_new ("wrote_chunk",
134 G_OBJECT_CLASS_TYPE (object_class),
136 G_STRUCT_OFFSET (SoupMessageClass, wrote_chunk),
138 soup_marshal_NONE__NONE,
140 signals[WROTE_BODY] =
141 g_signal_new ("wrote_body",
142 G_OBJECT_CLASS_TYPE (object_class),
144 G_STRUCT_OFFSET (SoupMessageClass, wrote_body),
146 soup_marshal_NONE__NONE,
149 signals[GOT_INFORMATIONAL] =
150 g_signal_new ("got_informational",
151 G_OBJECT_CLASS_TYPE (object_class),
153 G_STRUCT_OFFSET (SoupMessageClass, got_informational),
155 soup_marshal_NONE__NONE,
157 signals[GOT_HEADERS] =
158 g_signal_new ("got_headers",
159 G_OBJECT_CLASS_TYPE (object_class),
161 G_STRUCT_OFFSET (SoupMessageClass, got_headers),
163 soup_marshal_NONE__NONE,
166 g_signal_new ("got_chunk",
167 G_OBJECT_CLASS_TYPE (object_class),
169 G_STRUCT_OFFSET (SoupMessageClass, got_chunk),
171 soup_marshal_NONE__NONE,
174 g_signal_new ("got_body",
175 G_OBJECT_CLASS_TYPE (object_class),
177 G_STRUCT_OFFSET (SoupMessageClass, got_body),
179 soup_marshal_NONE__NONE,
183 g_signal_new ("restarted",
184 G_OBJECT_CLASS_TYPE (object_class),
186 G_STRUCT_OFFSET (SoupMessageClass, restarted),
188 soup_marshal_NONE__NONE,
191 g_signal_new ("finished",
192 G_OBJECT_CLASS_TYPE (object_class),
194 G_STRUCT_OFFSET (SoupMessageClass, finished),
196 soup_marshal_NONE__NONE,
200 SOUP_MAKE_TYPE (soup_message, SoupMessage, class_init, init, PARENT_TYPE)
205 * @method: the HTTP method for the created request
206 * @uri_string: the destination endpoint (as a string)
208 * Creates a new empty #SoupMessage, which will connect to @uri
210 * Return value: the new #SoupMessage (or %NULL if @uri could not
214 soup_message_new (const char *method, const char *uri_string)
219 uri = soup_uri_new (uri_string);
223 msg = g_object_new (SOUP_TYPE_MESSAGE, NULL);
224 msg->method = method ? method : SOUP_METHOD_GET;
225 msg->priv->uri = uri;
231 * soup_message_new_from_uri:
232 * @method: the HTTP method for the created request
233 * @uri: the destination endpoint (as a #SoupUri)
235 * Creates a new empty #SoupMessage, which will connect to @uri
237 * Return value: the new #SoupMessage
240 soup_message_new_from_uri (const char *method, const SoupUri *uri)
244 msg = g_object_new (SOUP_TYPE_MESSAGE, NULL);
245 msg->method = method ? method : SOUP_METHOD_GET;
246 msg->priv->uri = soup_uri_copy (uri);
252 * soup_message_set_request:
254 * @content_type: MIME Content-Type of the body
255 * @req_owner: the #SoupOwnership of the passed data buffer.
256 * @req_body: a data buffer containing the body of the message request.
257 * @req_length: the byte length of @req_body.
259 * Convenience function to set the request body of a #SoupMessage
262 soup_message_set_request (SoupMessage *msg,
263 const char *content_type,
264 SoupOwnership req_owner,
268 g_return_if_fail (SOUP_IS_MESSAGE (msg));
269 g_return_if_fail (content_type != NULL);
270 g_return_if_fail (req_body != NULL || req_length == 0);
272 soup_message_add_header (msg->request_headers,
273 "Content-Type", content_type);
274 msg->request.owner = req_owner;
275 msg->request.body = req_body;
276 msg->request.length = req_length;
280 * soup_message_set_response:
282 * @content_type: MIME Content-Type of the body
283 * @resp_owner: the #SoupOwnership of the passed data buffer.
284 * @resp_body: a data buffer containing the body of the message response.
285 * @resp_length: the byte length of @resp_body.
287 * Convenience function to set the response body of a #SoupMessage
290 soup_message_set_response (SoupMessage *msg,
291 const char *content_type,
292 SoupOwnership resp_owner,
296 g_return_if_fail (SOUP_IS_MESSAGE (msg));
297 g_return_if_fail (content_type != NULL);
298 g_return_if_fail (resp_body != NULL || resp_length == 0);
300 soup_message_add_header (msg->response_headers,
301 "Content-Type", content_type);
302 msg->response.owner = resp_owner;
303 msg->response.body = resp_body;
304 msg->response.length = resp_length;
308 soup_message_wrote_informational (SoupMessage *msg)
310 g_signal_emit (msg, signals[WROTE_INFORMATIONAL], 0);
314 soup_message_wrote_headers (SoupMessage *msg)
316 g_signal_emit (msg, signals[WROTE_HEADERS], 0);
320 soup_message_wrote_chunk (SoupMessage *msg)
322 g_signal_emit (msg, signals[WROTE_CHUNK], 0);
326 wrote_body (SoupMessage *req)
329 soup_message_run_handlers (req, SOUP_HANDLER_POST_REQUEST);
330 g_object_unref (req);
334 soup_message_wrote_body (SoupMessage *msg)
336 g_signal_emit (msg, signals[WROTE_BODY], 0);
340 soup_message_got_informational (SoupMessage *msg)
342 g_signal_emit (msg, signals[GOT_INFORMATIONAL], 0);
346 got_headers (SoupMessage *req)
349 soup_message_run_handlers (req, SOUP_HANDLER_PRE_BODY);
350 if (SOUP_MESSAGE_IS_STARTING (req))
351 g_signal_stop_emission (req, signals[GOT_HEADERS], 0);
352 g_object_unref (req);
356 soup_message_got_headers (SoupMessage *msg)
358 g_signal_emit (msg, signals[GOT_HEADERS], 0);
362 got_chunk (SoupMessage *req)
365 soup_message_run_handlers (req, SOUP_HANDLER_BODY_CHUNK);
366 if (SOUP_MESSAGE_IS_STARTING (req))
367 g_signal_stop_emission (req, signals[GOT_CHUNK], 0);
368 g_object_unref (req);
372 soup_message_got_chunk (SoupMessage *msg)
374 g_signal_emit (msg, signals[GOT_CHUNK], 0);
378 got_body (SoupMessage *req)
381 soup_message_run_handlers (req, SOUP_HANDLER_POST_BODY);
382 if (SOUP_MESSAGE_IS_STARTING (req))
383 g_signal_stop_emission (req, signals[GOT_BODY], 0);
384 g_object_unref (req);
388 soup_message_got_body (SoupMessage *msg)
390 g_signal_emit (msg, signals[GOT_BODY], 0);
394 restarted (SoupMessage *req)
396 soup_message_io_cancel (req);
400 soup_message_restarted (SoupMessage *msg)
402 g_signal_emit (msg, signals[RESTARTED], 0);
406 finished (SoupMessage *req)
408 soup_message_io_cancel (req);
409 req->status = SOUP_MESSAGE_STATUS_FINISHED;
413 soup_message_finished (SoupMessage *msg)
415 g_signal_emit (msg, signals[FINISHED], 0);
419 free_header_list (gpointer name, gpointer vals, gpointer user_data)
422 g_slist_foreach (vals, (GFunc) g_free, NULL);
429 soup_message_clear_headers (GHashTable *hash)
431 g_return_if_fail (hash != NULL);
433 g_hash_table_foreach_remove (hash, free_header_list, NULL);
437 soup_message_remove_header (GHashTable *hash, const char *name)
439 gpointer old_key, old_vals;
441 g_return_if_fail (hash != NULL);
442 g_return_if_fail (name != NULL || name[0] != '\0');
444 if (g_hash_table_lookup_extended (hash, name, &old_key, &old_vals)) {
445 g_hash_table_remove (hash, name);
446 free_header_list (old_key, old_vals, NULL);
451 soup_message_add_header (GHashTable *hash, const char *name, const char *value)
455 g_return_if_fail (hash != NULL);
456 g_return_if_fail (name != NULL || name [0] != '\0');
457 g_return_if_fail (value != NULL);
459 old_value = g_hash_table_lookup (hash, name);
462 g_slist_append (old_value, g_strdup (value));
464 g_hash_table_insert (hash, g_strdup (name),
465 g_slist_append (NULL, g_strdup (value)));
470 * soup_message_get_header:
471 * @hash: a header hash table
472 * @name: header name.
474 * Lookup the first transport header in @hash with a key equal to
477 * Return value: the header's value or %NULL if not found.
480 soup_message_get_header (GHashTable *hash, const char *name)
484 g_return_val_if_fail (hash != NULL, NULL);
485 g_return_val_if_fail (name != NULL || name [0] != '\0', NULL);
487 vals = g_hash_table_lookup (hash, name);
495 * soup_message_get_header_list:
496 * @hash: a header hash table
497 * @name: header name.
499 * Lookup the all transport request headers in @hash with a key equal
502 * Return value: a const pointer to a #GSList of header values or
503 * %NULL if not found.
506 soup_message_get_header_list (GHashTable *hash, const char *name)
508 g_return_val_if_fail (hash != NULL, NULL);
509 g_return_val_if_fail (name != NULL || name [0] != '\0', NULL);
511 return g_hash_table_lookup (hash, name);
517 } SoupMessageForeachHeaderData;
520 foreach_value_in_list (gpointer name, gpointer value, gpointer user_data)
522 GSList *vals = value;
523 SoupMessageForeachHeaderData *data = user_data;
526 (*data->func) (name, vals->data, data->user_data);
532 soup_message_foreach_header (GHashTable *hash, GHFunc func, gpointer user_data)
534 SoupMessageForeachHeaderData data;
536 g_return_if_fail (hash != NULL);
537 g_return_if_fail (func != NULL);
540 data.user_data = user_data;
541 g_hash_table_foreach (hash, foreach_value_in_list, &data);
545 soup_message_cleanup_response (SoupMessage *req)
547 if (req->response.owner == SOUP_BUFFER_SYSTEM_OWNED)
548 g_free (req->response.body);
550 req->response.owner = 0;
551 req->response.body = NULL;
552 req->response.length = 0;
556 soup_message_clear_headers (req->response_headers);
558 req->status_code = 0;
559 if (req->reason_phrase) {
560 g_free ((char *) req->reason_phrase);
561 req->reason_phrase = NULL;
566 soup_message_set_flags (SoupMessage *msg, guint flags)
568 g_return_if_fail (SOUP_IS_MESSAGE (msg));
570 msg->priv->msg_flags = flags;
574 soup_message_get_flags (SoupMessage *msg)
576 g_return_val_if_fail (SOUP_IS_MESSAGE (msg), 0);
578 return msg->priv->msg_flags;
582 soup_message_set_http_version (SoupMessage *msg, SoupHttpVersion version)
584 g_return_if_fail (SOUP_IS_MESSAGE (msg));
586 msg->priv->http_version = version;
590 soup_message_get_http_version (SoupMessage *msg)
592 g_return_val_if_fail (SOUP_IS_MESSAGE (msg), SOUP_HTTP_1_0);
594 return msg->priv->http_version;
598 soup_message_is_keepalive (SoupMessage *msg)
600 const char *c_conn, *s_conn;
602 c_conn = soup_message_get_header (msg->request_headers, "Connection");
603 s_conn = soup_message_get_header (msg->response_headers, "Connection");
605 if (msg->priv->http_version == SOUP_HTTP_1_0) {
606 /* Only persistent if the client requested keepalive
607 * and the server agreed.
610 if (!c_conn || !s_conn)
612 if (g_strcasecmp (c_conn, "Keep-Alive") != 0 ||
613 g_strcasecmp (s_conn, "Keep-Alive") != 0)
618 /* Persistent unless either side requested otherwise */
620 if (c_conn && g_strcasecmp (c_conn, "close") == 0)
622 if (s_conn && g_strcasecmp (s_conn, "close") == 0)
630 soup_message_set_uri (SoupMessage *msg, const SoupUri *new_uri)
632 g_return_if_fail (SOUP_IS_MESSAGE (msg));
634 if (msg->priv->uri && new_uri) {
635 if (strcmp (msg->priv->uri->host, new_uri->host) != 0)
636 soup_message_io_cancel (msg);
638 soup_message_io_cancel (msg);
641 soup_uri_free (msg->priv->uri);
642 msg->priv->uri = soup_uri_copy (new_uri);
646 soup_message_get_uri (SoupMessage *msg)
648 g_return_val_if_fail (SOUP_IS_MESSAGE (msg), NULL);
650 return msg->priv->uri;
654 soup_message_set_status (SoupMessage *msg, guint status_code)
656 g_return_if_fail (SOUP_IS_MESSAGE (msg));
657 g_return_if_fail (status_code != 0);
659 g_free ((char *) msg->reason_phrase);
661 msg->status_code = status_code;
662 msg->reason_phrase = g_strdup (soup_status_get_phrase (status_code));
666 soup_message_set_status_full (SoupMessage *msg,
668 const char *reason_phrase)
670 g_return_if_fail (SOUP_IS_MESSAGE (msg));
671 g_return_if_fail (status_code != 0);
672 g_return_if_fail (reason_phrase != NULL);
674 g_free ((char *) msg->reason_phrase);
676 msg->status_code = status_code;
677 msg->reason_phrase = g_strdup (reason_phrase);
682 soup_message_add_chunk (SoupMessage *msg,
687 SoupDataBuffer *chunk;
689 g_return_if_fail (SOUP_IS_MESSAGE (msg));
690 g_return_if_fail (body != NULL || length == 0);
692 chunk = g_new0 (SoupDataBuffer, 1);
693 if (owner == SOUP_BUFFER_USER_OWNED) {
694 chunk->owner = SOUP_BUFFER_SYSTEM_OWNED;
695 chunk->body = g_memdup (body, length);
697 chunk->owner = owner;
698 chunk->body = (char *)body;
700 chunk->length = length;
702 if (msg->priv->chunks) {
703 g_slist_append (msg->priv->last_chunk, chunk);
704 msg->priv->last_chunk = msg->priv->last_chunk->next;
706 msg->priv->chunks = msg->priv->last_chunk =
707 g_slist_append (NULL, chunk);
712 soup_message_add_final_chunk (SoupMessage *msg)
714 soup_message_add_chunk (msg, SOUP_BUFFER_STATIC, NULL, 0);
718 soup_message_pop_chunk (SoupMessage *msg)
720 SoupDataBuffer *chunk;
722 g_return_val_if_fail (SOUP_IS_MESSAGE (msg), NULL);
724 if (!msg->priv->chunks)
727 chunk = msg->priv->chunks->data;
728 msg->priv->chunks = g_slist_remove (msg->priv->chunks, chunk);
729 if (!msg->priv->chunks)
730 msg->priv->last_chunk = NULL;
736 free_chunks (SoupMessage *msg)
738 SoupDataBuffer *chunk;
741 for (ch = msg->priv->chunks; ch; ch = ch->next) {
744 if (chunk->owner == SOUP_BUFFER_SYSTEM_OWNED)
745 g_free (chunk->body);
749 g_slist_free (msg->priv->chunks);
750 msg->priv->chunks = msg->priv->last_chunk = NULL;