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 stop_io (SoupMessage *req);
44 static void free_chunks (SoupMessage *msg);
47 init (GObject *object)
49 SoupMessage *msg = SOUP_MESSAGE (object);
51 msg->priv = g_new0 (SoupMessagePrivate, 1);
53 msg->status = SOUP_MESSAGE_STATUS_IDLE;
55 msg->request_headers = g_hash_table_new (soup_str_case_hash,
58 msg->response_headers = g_hash_table_new (soup_str_case_hash,
61 msg->priv->http_version = SOUP_HTTP_1_1;
65 finalize (GObject *object)
67 SoupMessage *msg = SOUP_MESSAGE (object);
69 soup_message_io_cancel (msg);
72 soup_uri_free (msg->priv->uri);
74 if (msg->request.owner == SOUP_BUFFER_SYSTEM_OWNED)
75 g_free (msg->request.body);
76 if (msg->response.owner == SOUP_BUFFER_SYSTEM_OWNED)
77 g_free (msg->response.body);
80 soup_message_clear_headers (msg->request_headers);
81 g_hash_table_destroy (msg->request_headers);
83 soup_message_clear_headers (msg->response_headers);
84 g_hash_table_destroy (msg->response_headers);
86 g_slist_foreach (msg->priv->content_handlers, (GFunc) g_free, NULL);
87 g_slist_free (msg->priv->content_handlers);
89 g_free ((char *) msg->reason_phrase);
93 G_OBJECT_CLASS (parent_class)->finalize (object);
97 class_init (GObjectClass *object_class)
99 SoupMessageClass *message_class = SOUP_MESSAGE_CLASS (object_class);
101 parent_class = g_type_class_ref (PARENT_TYPE);
103 /* virtual method definition */
104 message_class->wrote_body = wrote_body;
105 message_class->got_headers = got_headers;
106 message_class->got_chunk = got_chunk;
107 message_class->got_body = got_body;
108 message_class->restarted = stop_io;
109 message_class->finished = stop_io;
111 /* virtual method override */
112 object_class->finalize = finalize;
115 signals[WROTE_INFORMATIONAL] =
116 g_signal_new ("wrote_informational",
117 G_OBJECT_CLASS_TYPE (object_class),
119 G_STRUCT_OFFSET (SoupMessageClass, wrote_informational),
121 soup_marshal_NONE__NONE,
123 signals[WROTE_HEADERS] =
124 g_signal_new ("wrote_headers",
125 G_OBJECT_CLASS_TYPE (object_class),
127 G_STRUCT_OFFSET (SoupMessageClass, wrote_headers),
129 soup_marshal_NONE__NONE,
131 signals[WROTE_CHUNK] =
132 g_signal_new ("wrote_chunk",
133 G_OBJECT_CLASS_TYPE (object_class),
135 G_STRUCT_OFFSET (SoupMessageClass, wrote_chunk),
137 soup_marshal_NONE__NONE,
139 signals[WROTE_BODY] =
140 g_signal_new ("wrote_body",
141 G_OBJECT_CLASS_TYPE (object_class),
143 G_STRUCT_OFFSET (SoupMessageClass, wrote_body),
145 soup_marshal_NONE__NONE,
148 signals[GOT_INFORMATIONAL] =
149 g_signal_new ("got_informational",
150 G_OBJECT_CLASS_TYPE (object_class),
152 G_STRUCT_OFFSET (SoupMessageClass, got_informational),
154 soup_marshal_NONE__NONE,
156 signals[GOT_HEADERS] =
157 g_signal_new ("got_headers",
158 G_OBJECT_CLASS_TYPE (object_class),
160 G_STRUCT_OFFSET (SoupMessageClass, got_headers),
162 soup_marshal_NONE__NONE,
165 g_signal_new ("got_chunk",
166 G_OBJECT_CLASS_TYPE (object_class),
168 G_STRUCT_OFFSET (SoupMessageClass, got_chunk),
170 soup_marshal_NONE__NONE,
173 g_signal_new ("got_body",
174 G_OBJECT_CLASS_TYPE (object_class),
176 G_STRUCT_OFFSET (SoupMessageClass, got_body),
178 soup_marshal_NONE__NONE,
182 g_signal_new ("restarted",
183 G_OBJECT_CLASS_TYPE (object_class),
185 G_STRUCT_OFFSET (SoupMessageClass, restarted),
187 soup_marshal_NONE__NONE,
190 g_signal_new ("finished",
191 G_OBJECT_CLASS_TYPE (object_class),
193 G_STRUCT_OFFSET (SoupMessageClass, finished),
195 soup_marshal_NONE__NONE,
199 SOUP_MAKE_TYPE (soup_message, SoupMessage, class_init, init, PARENT_TYPE)
204 * @method: the HTTP method for the created request
205 * @uri_string: the destination endpoint (as a string)
207 * Creates a new empty #SoupMessage, which will connect to @uri
209 * Return value: the new #SoupMessage (or %NULL if @uri could not
213 soup_message_new (const char *method, const char *uri_string)
218 uri = soup_uri_new (uri_string);
222 msg = g_object_new (SOUP_TYPE_MESSAGE, NULL);
223 msg->method = method ? method : SOUP_METHOD_GET;
224 msg->priv->uri = uri;
230 * soup_message_new_from_uri:
231 * @method: the HTTP method for the created request
232 * @uri: the destination endpoint (as a #SoupUri)
234 * Creates a new empty #SoupMessage, which will connect to @uri
236 * Return value: the new #SoupMessage
239 soup_message_new_from_uri (const char *method, const SoupUri *uri)
243 msg = g_object_new (SOUP_TYPE_MESSAGE, NULL);
244 msg->method = method ? method : SOUP_METHOD_GET;
245 msg->priv->uri = soup_uri_copy (uri);
251 * soup_message_set_request:
253 * @content_type: MIME Content-Type of the body
254 * @req_owner: the #SoupOwnership of the passed data buffer.
255 * @req_body: a data buffer containing the body of the message request.
256 * @req_length: the byte length of @req_body.
258 * Convenience function to set the request body of a #SoupMessage
261 soup_message_set_request (SoupMessage *msg,
262 const char *content_type,
263 SoupOwnership req_owner,
267 g_return_if_fail (SOUP_IS_MESSAGE (msg));
268 g_return_if_fail (content_type != NULL);
269 g_return_if_fail (req_body != NULL || req_length == 0);
271 soup_message_add_header (msg->request_headers,
272 "Content-Type", content_type);
273 msg->request.owner = req_owner;
274 msg->request.body = req_body;
275 msg->request.length = req_length;
279 * soup_message_set_response:
281 * @content_type: MIME Content-Type of the body
282 * @resp_owner: the #SoupOwnership of the passed data buffer.
283 * @resp_body: a data buffer containing the body of the message response.
284 * @resp_length: the byte length of @resp_body.
286 * Convenience function to set the response body of a #SoupMessage
289 soup_message_set_response (SoupMessage *msg,
290 const char *content_type,
291 SoupOwnership resp_owner,
295 g_return_if_fail (SOUP_IS_MESSAGE (msg));
296 g_return_if_fail (content_type != NULL);
297 g_return_if_fail (resp_body != NULL || resp_length == 0);
299 soup_message_add_header (msg->response_headers,
300 "Content-Type", content_type);
301 msg->response.owner = resp_owner;
302 msg->response.body = resp_body;
303 msg->response.length = resp_length;
307 soup_message_wrote_informational (SoupMessage *msg)
309 g_signal_emit (msg, signals[WROTE_INFORMATIONAL], 0);
313 soup_message_wrote_headers (SoupMessage *msg)
315 g_signal_emit (msg, signals[WROTE_HEADERS], 0);
319 soup_message_wrote_chunk (SoupMessage *msg)
321 g_signal_emit (msg, signals[WROTE_CHUNK], 0);
325 wrote_body (SoupMessage *req)
328 soup_message_run_handlers (req, SOUP_HANDLER_POST_REQUEST);
329 g_object_unref (req);
333 soup_message_wrote_body (SoupMessage *msg)
335 g_signal_emit (msg, signals[WROTE_BODY], 0);
339 soup_message_got_informational (SoupMessage *msg)
341 g_signal_emit (msg, signals[GOT_INFORMATIONAL], 0);
345 got_headers (SoupMessage *req)
348 soup_message_run_handlers (req, SOUP_HANDLER_PRE_BODY);
349 if (SOUP_MESSAGE_IS_STARTING (req))
350 g_signal_stop_emission (req, signals[GOT_HEADERS], 0);
351 g_object_unref (req);
355 soup_message_got_headers (SoupMessage *msg)
357 g_signal_emit (msg, signals[GOT_HEADERS], 0);
361 got_chunk (SoupMessage *req)
364 soup_message_run_handlers (req, SOUP_HANDLER_BODY_CHUNK);
365 if (SOUP_MESSAGE_IS_STARTING (req))
366 g_signal_stop_emission (req, signals[GOT_CHUNK], 0);
367 g_object_unref (req);
371 soup_message_got_chunk (SoupMessage *msg)
373 g_signal_emit (msg, signals[GOT_CHUNK], 0);
377 got_body (SoupMessage *req)
380 soup_message_run_handlers (req, SOUP_HANDLER_POST_BODY);
381 if (SOUP_MESSAGE_IS_STARTING (req))
382 g_signal_stop_emission (req, signals[GOT_BODY], 0);
383 g_object_unref (req);
387 soup_message_got_body (SoupMessage *msg)
389 g_signal_emit (msg, signals[GOT_BODY], 0);
393 stop_io (SoupMessage *req)
395 soup_message_io_cancel (req);
399 soup_message_restarted (SoupMessage *msg)
401 g_signal_emit (msg, signals[RESTARTED], 0);
405 soup_message_finished (SoupMessage *msg)
407 g_signal_emit (msg, signals[FINISHED], 0);
411 * soup_message_cancel:
412 * @msg: a #SoupMessage currently being processed.
414 * Cancel a running message, and issue completion callback with an
415 * status code of %SOUP_STATUS_CANCELLED. If not requeued by the
416 * completion callback, the @msg will be destroyed.
419 soup_message_cancel (SoupMessage *msg)
421 if (msg->status != SOUP_MESSAGE_STATUS_FINISHED) {
422 soup_message_set_status (msg, SOUP_STATUS_CANCELLED);
423 soup_message_finished (msg);
428 free_header_list (gpointer name, gpointer vals, gpointer user_data)
431 g_slist_foreach (vals, (GFunc) g_free, NULL);
438 soup_message_clear_headers (GHashTable *hash)
440 g_return_if_fail (hash != NULL);
442 g_hash_table_foreach_remove (hash, free_header_list, NULL);
446 soup_message_remove_header (GHashTable *hash, const char *name)
448 gpointer old_key, old_vals;
450 g_return_if_fail (hash != NULL);
451 g_return_if_fail (name != NULL || name[0] != '\0');
453 if (g_hash_table_lookup_extended (hash, name, &old_key, &old_vals)) {
454 g_hash_table_remove (hash, name);
455 free_header_list (old_key, old_vals, NULL);
460 soup_message_add_header (GHashTable *hash, const char *name, const char *value)
464 g_return_if_fail (hash != NULL);
465 g_return_if_fail (name != NULL || name [0] != '\0');
466 g_return_if_fail (value != NULL);
468 old_value = g_hash_table_lookup (hash, name);
471 g_slist_append (old_value, g_strdup (value));
473 g_hash_table_insert (hash, g_strdup (name),
474 g_slist_append (NULL, g_strdup (value)));
479 * soup_message_get_header:
480 * @hash: a header hash table
481 * @name: header name.
483 * Lookup the first transport header in @hash with a key equal to
486 * Return value: the header's value or %NULL if not found.
489 soup_message_get_header (GHashTable *hash, const char *name)
493 g_return_val_if_fail (hash != NULL, NULL);
494 g_return_val_if_fail (name != NULL || name [0] != '\0', NULL);
496 vals = g_hash_table_lookup (hash, name);
504 * soup_message_get_header_list:
505 * @hash: a header hash table
506 * @name: header name.
508 * Lookup the all transport request headers in @hash with a key equal
511 * Return value: a const pointer to a #GSList of header values or
512 * %NULL if not found.
515 soup_message_get_header_list (GHashTable *hash, const char *name)
517 g_return_val_if_fail (hash != NULL, NULL);
518 g_return_val_if_fail (name != NULL || name [0] != '\0', NULL);
520 return g_hash_table_lookup (hash, name);
526 } SoupMessageForeachHeaderData;
529 foreach_value_in_list (gpointer name, gpointer value, gpointer user_data)
531 GSList *vals = value;
532 SoupMessageForeachHeaderData *data = user_data;
535 (*data->func) (name, vals->data, data->user_data);
541 soup_message_foreach_header (GHashTable *hash, GHFunc func, gpointer user_data)
543 SoupMessageForeachHeaderData data;
545 g_return_if_fail (hash != NULL);
546 g_return_if_fail (func != NULL);
549 data.user_data = user_data;
550 g_hash_table_foreach (hash, foreach_value_in_list, &data);
554 soup_message_cleanup_response (SoupMessage *req)
556 if (req->response.owner == SOUP_BUFFER_SYSTEM_OWNED)
557 g_free (req->response.body);
559 req->response.owner = 0;
560 req->response.body = NULL;
561 req->response.length = 0;
565 soup_message_clear_headers (req->response_headers);
567 req->status_code = 0;
568 if (req->reason_phrase) {
569 g_free ((char *) req->reason_phrase);
570 req->reason_phrase = NULL;
575 soup_message_set_flags (SoupMessage *msg, guint flags)
577 g_return_if_fail (SOUP_IS_MESSAGE (msg));
579 msg->priv->msg_flags = flags;
583 soup_message_get_flags (SoupMessage *msg)
585 g_return_val_if_fail (SOUP_IS_MESSAGE (msg), 0);
587 return msg->priv->msg_flags;
591 soup_message_set_http_version (SoupMessage *msg, SoupHttpVersion version)
593 g_return_if_fail (SOUP_IS_MESSAGE (msg));
595 msg->priv->http_version = version;
599 soup_message_get_http_version (SoupMessage *msg)
601 g_return_val_if_fail (SOUP_IS_MESSAGE (msg), SOUP_HTTP_1_0);
603 return msg->priv->http_version;
607 soup_message_is_keepalive (SoupMessage *msg)
609 const char *c_conn, *s_conn;
611 c_conn = soup_message_get_header (msg->request_headers, "Connection");
612 s_conn = soup_message_get_header (msg->response_headers, "Connection");
614 if (msg->priv->http_version == SOUP_HTTP_1_0) {
615 /* Only persistent if the client requested keepalive
616 * and the server agreed.
619 if (!c_conn || !s_conn)
621 if (g_strcasecmp (c_conn, "Keep-Alive") != 0 ||
622 g_strcasecmp (s_conn, "Keep-Alive") != 0)
627 /* Persistent unless either side requested otherwise */
629 if (c_conn && g_strcasecmp (c_conn, "close") == 0)
631 if (s_conn && g_strcasecmp (s_conn, "close") == 0)
639 soup_message_set_uri (SoupMessage *msg, const SoupUri *new_uri)
641 g_return_if_fail (SOUP_IS_MESSAGE (msg));
643 if (msg->priv->uri && new_uri) {
644 if (strcmp (msg->priv->uri->host, new_uri->host) != 0)
645 soup_message_io_cancel (msg);
647 soup_message_io_cancel (msg);
650 soup_uri_free (msg->priv->uri);
651 msg->priv->uri = soup_uri_copy (new_uri);
655 soup_message_get_uri (SoupMessage *msg)
657 g_return_val_if_fail (SOUP_IS_MESSAGE (msg), NULL);
659 return msg->priv->uri;
663 soup_message_set_status (SoupMessage *msg, guint status_code)
665 g_return_if_fail (SOUP_IS_MESSAGE (msg));
666 g_return_if_fail (status_code != 0);
668 g_free ((char *) msg->reason_phrase);
670 msg->status_code = status_code;
671 msg->reason_phrase = g_strdup (soup_status_get_phrase (status_code));
675 soup_message_set_status_full (SoupMessage *msg,
677 const char *reason_phrase)
679 g_return_if_fail (SOUP_IS_MESSAGE (msg));
680 g_return_if_fail (status_code != 0);
681 g_return_if_fail (reason_phrase != NULL);
683 g_free ((char *) msg->reason_phrase);
685 msg->status_code = status_code;
686 msg->reason_phrase = g_strdup (reason_phrase);
691 soup_message_add_chunk (SoupMessage *msg,
696 SoupDataBuffer *chunk;
698 g_return_if_fail (SOUP_IS_MESSAGE (msg));
699 g_return_if_fail (body != NULL || length == 0);
701 chunk = g_new0 (SoupDataBuffer, 1);
702 if (owner == SOUP_BUFFER_USER_OWNED) {
703 chunk->owner = SOUP_BUFFER_SYSTEM_OWNED;
704 chunk->body = g_memdup (body, length);
706 chunk->owner = owner;
707 chunk->body = (char *)body;
709 chunk->length = length;
711 if (msg->priv->chunks) {
712 g_slist_append (msg->priv->last_chunk, chunk);
713 msg->priv->last_chunk = msg->priv->last_chunk->next;
715 msg->priv->chunks = msg->priv->last_chunk =
716 g_slist_append (NULL, chunk);
721 soup_message_add_final_chunk (SoupMessage *msg)
723 soup_message_add_chunk (msg, SOUP_BUFFER_STATIC, NULL, 0);
727 soup_message_pop_chunk (SoupMessage *msg)
729 SoupDataBuffer *chunk;
731 g_return_val_if_fail (SOUP_IS_MESSAGE (msg), NULL);
733 if (!msg->priv->chunks)
736 chunk = msg->priv->chunks->data;
737 msg->priv->chunks = g_slist_remove (msg->priv->chunks, chunk);
738 if (!msg->priv->chunks)
739 msg->priv->last_chunk = NULL;
745 free_chunks (SoupMessage *msg)
747 SoupDataBuffer *chunk;
750 for (ch = msg->priv->chunks; ch; ch = ch->next) {
753 if (chunk->owner == SOUP_BUFFER_SYSTEM_OWNED)
754 g_free (chunk->body);
758 g_slist_free (msg->priv->chunks);
759 msg->priv->chunks = msg->priv->last_chunk = NULL;