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"
15 #include "soup-private.h"
17 #define PARENT_TYPE G_TYPE_OBJECT
18 static GObjectClass *parent_class;
34 static guint signals[LAST_SIGNAL] = { 0 };
36 static void got_headers (SoupMessage *req);
37 static void got_chunk (SoupMessage *req);
38 static void got_body (SoupMessage *req);
39 static void finished (SoupMessage *req);
40 static void free_chunks (SoupMessage *msg);
43 init (GObject *object)
45 SoupMessage *msg = SOUP_MESSAGE (object);
47 msg->priv = g_new0 (SoupMessagePrivate, 1);
49 msg->status = SOUP_MESSAGE_STATUS_IDLE;
51 msg->request_headers = g_hash_table_new (soup_str_case_hash,
54 msg->response_headers = g_hash_table_new (soup_str_case_hash,
57 msg->priv->http_version = SOUP_HTTP_1_1;
61 finalize (GObject *object)
63 SoupMessage *msg = SOUP_MESSAGE (object);
65 soup_message_io_cancel (msg);
68 soup_uri_free (msg->priv->uri);
70 if (msg->request.owner == SOUP_BUFFER_SYSTEM_OWNED)
71 g_free (msg->request.body);
72 if (msg->response.owner == SOUP_BUFFER_SYSTEM_OWNED)
73 g_free (msg->response.body);
76 soup_message_clear_headers (msg->request_headers);
77 g_hash_table_destroy (msg->request_headers);
79 soup_message_clear_headers (msg->response_headers);
80 g_hash_table_destroy (msg->response_headers);
82 g_slist_foreach (msg->priv->content_handlers, (GFunc) g_free, NULL);
83 g_slist_free (msg->priv->content_handlers);
85 g_free ((char *) msg->reason_phrase);
89 G_OBJECT_CLASS (parent_class)->finalize (object);
93 class_init (GObjectClass *object_class)
95 SoupMessageClass *message_class = SOUP_MESSAGE_CLASS (object_class);
97 parent_class = g_type_class_ref (PARENT_TYPE);
99 /* virtual method definition */
100 message_class->got_headers = got_headers;
101 message_class->got_chunk = got_chunk;
102 message_class->got_body = got_body;
103 message_class->finished = finished;
105 /* virtual method override */
106 object_class->finalize = finalize;
109 signals[WROTE_HEADERS] =
110 g_signal_new ("wrote_headers",
111 G_OBJECT_CLASS_TYPE (object_class),
113 G_STRUCT_OFFSET (SoupMessageClass, wrote_headers),
115 soup_marshal_NONE__NONE,
117 signals[WROTE_CHUNK] =
118 g_signal_new ("wrote_chunk",
119 G_OBJECT_CLASS_TYPE (object_class),
121 G_STRUCT_OFFSET (SoupMessageClass, wrote_chunk),
123 soup_marshal_NONE__NONE,
125 signals[WROTE_BODY] =
126 g_signal_new ("wrote_body",
127 G_OBJECT_CLASS_TYPE (object_class),
129 G_STRUCT_OFFSET (SoupMessageClass, wrote_body),
131 soup_marshal_NONE__NONE,
134 signals[GOT_HEADERS] =
135 g_signal_new ("got_headers",
136 G_OBJECT_CLASS_TYPE (object_class),
138 G_STRUCT_OFFSET (SoupMessageClass, got_headers),
140 soup_marshal_NONE__NONE,
143 g_signal_new ("got_chunk",
144 G_OBJECT_CLASS_TYPE (object_class),
146 G_STRUCT_OFFSET (SoupMessageClass, got_chunk),
148 soup_marshal_NONE__NONE,
151 g_signal_new ("got_body",
152 G_OBJECT_CLASS_TYPE (object_class),
154 G_STRUCT_OFFSET (SoupMessageClass, got_body),
156 soup_marshal_NONE__NONE,
160 g_signal_new ("finished",
161 G_OBJECT_CLASS_TYPE (object_class),
163 G_STRUCT_OFFSET (SoupMessageClass, finished),
165 soup_marshal_NONE__NONE,
169 SOUP_MAKE_TYPE (soup_message, SoupMessage, class_init, init, PARENT_TYPE)
174 * @method: the HTTP method for the created request
175 * @uri_string: the destination endpoint (as a string)
177 * Creates a new empty #SoupMessage, which will connect to @uri
179 * Return value: the new #SoupMessage (or %NULL if @uri could not
183 soup_message_new (const char *method, const char *uri_string)
188 uri = soup_uri_new (uri_string);
192 msg = g_object_new (SOUP_TYPE_MESSAGE, NULL);
193 msg->method = method ? method : SOUP_METHOD_GET;
194 msg->priv->uri = uri;
200 * soup_message_new_from_uri:
201 * @method: the HTTP method for the created request
202 * @uri: the destination endpoint (as a #SoupUri)
204 * Creates a new empty #SoupMessage, which will connect to @uri
206 * Return value: the new #SoupMessage
209 soup_message_new_from_uri (const char *method, const SoupUri *uri)
213 msg = g_object_new (SOUP_TYPE_MESSAGE, NULL);
214 msg->method = method ? method : SOUP_METHOD_GET;
215 msg->priv->uri = soup_uri_copy (uri);
221 * soup_message_set_request:
223 * @content_type: MIME Content-Type of the body
224 * @req_owner: the #SoupOwnership of the passed data buffer.
225 * @req_body: a data buffer containing the body of the message request.
226 * @req_length: the byte length of @req_body.
228 * Convenience function to set the request body of a #SoupMessage
231 soup_message_set_request (SoupMessage *msg,
232 const char *content_type,
233 SoupOwnership req_owner,
237 g_return_if_fail (SOUP_IS_MESSAGE (msg));
238 g_return_if_fail (content_type != NULL);
239 g_return_if_fail (req_body != NULL || req_length == 0);
241 soup_message_add_header (msg->request_headers,
242 "Content-Type", content_type);
243 msg->request.owner = req_owner;
244 msg->request.body = req_body;
245 msg->request.length = req_length;
249 * soup_message_set_response:
251 * @content_type: MIME Content-Type of the body
252 * @req_owner: the #SoupOwnership of the passed data buffer.
253 * @req_body: a data buffer containing the body of the message response.
254 * @req_length: the byte length of @req_body.
256 * Convenience function to set the response body of a #SoupMessage
259 soup_message_set_response (SoupMessage *msg,
260 const char *content_type,
261 SoupOwnership resp_owner,
265 g_return_if_fail (SOUP_IS_MESSAGE (msg));
266 g_return_if_fail (content_type != NULL);
267 g_return_if_fail (resp_body != NULL || resp_length == 0);
269 soup_message_add_header (msg->response_headers,
270 "Content-Type", content_type);
271 msg->response.owner = resp_owner;
272 msg->response.body = resp_body;
273 msg->response.length = resp_length;
277 soup_message_wrote_headers (SoupMessage *msg)
279 g_signal_emit (msg, signals[WROTE_HEADERS], 0);
283 soup_message_wrote_chunk (SoupMessage *msg)
285 g_signal_emit (msg, signals[WROTE_CHUNK], 0);
289 soup_message_wrote_body (SoupMessage *msg)
291 g_signal_emit (msg, signals[WROTE_BODY], 0);
295 got_headers (SoupMessage *req)
298 soup_message_run_handlers (req, SOUP_HANDLER_PRE_BODY);
299 if (SOUP_MESSAGE_IS_STARTING (req))
300 g_signal_stop_emission (req, signals[GOT_HEADERS], 0);
301 g_object_unref (req);
305 soup_message_got_headers (SoupMessage *msg)
307 g_signal_emit (msg, signals[GOT_HEADERS], 0);
311 got_chunk (SoupMessage *req)
314 soup_message_run_handlers (req, SOUP_HANDLER_BODY_CHUNK);
315 if (SOUP_MESSAGE_IS_STARTING (req))
316 g_signal_stop_emission (req, signals[GOT_CHUNK], 0);
317 g_object_unref (req);
321 soup_message_got_chunk (SoupMessage *msg)
323 g_signal_emit (msg, signals[GOT_CHUNK], 0);
327 got_body (SoupMessage *req)
330 soup_message_run_handlers (req, SOUP_HANDLER_POST_BODY);
331 if (SOUP_MESSAGE_IS_STARTING (req))
332 g_signal_stop_emission (req, signals[GOT_BODY], 0);
333 g_object_unref (req);
337 soup_message_got_body (SoupMessage *msg)
339 g_signal_emit (msg, signals[GOT_BODY], 0);
343 finished (SoupMessage *req)
345 soup_message_io_cancel (req);
349 soup_message_finished (SoupMessage *msg)
351 g_signal_emit (msg, signals[FINISHED], 0);
356 * soup_message_cancel:
357 * @msg: a #SoupMessage currently being processed.
359 * Cancel a running message, and issue completion callback with an
360 * status code of %SOUP_STATUS_CANCELLED. If not requeued by the
361 * completion callback, the @msg will be destroyed.
364 soup_message_cancel (SoupMessage *msg)
366 soup_message_set_status (msg, SOUP_STATUS_CANCELLED);
367 soup_message_finished (msg);
371 free_header_list (gpointer name, gpointer vals, gpointer user_data)
374 g_slist_foreach (vals, (GFunc) g_free, NULL);
381 soup_message_clear_headers (GHashTable *hash)
383 g_return_if_fail (hash != NULL);
385 g_hash_table_foreach_remove (hash, free_header_list, NULL);
389 soup_message_remove_header (GHashTable *hash, const char *name)
391 gpointer old_key, old_vals;
393 g_return_if_fail (hash != NULL);
394 g_return_if_fail (name != NULL || name[0] != '\0');
396 if (g_hash_table_lookup_extended (hash, name, &old_key, &old_vals)) {
397 g_hash_table_remove (hash, name);
398 free_header_list (old_key, old_vals, NULL);
403 soup_message_add_header (GHashTable *hash, const char *name, const char *value)
407 g_return_if_fail (hash != NULL);
408 g_return_if_fail (name != NULL || name [0] != '\0');
409 g_return_if_fail (value != NULL);
411 old_value = g_hash_table_lookup (hash, name);
414 g_slist_append (old_value, g_strdup (value));
416 g_hash_table_insert (hash, g_strdup (name),
417 g_slist_append (NULL, g_strdup (value)));
422 * soup_message_get_header:
423 * @hash: a header hash table
424 * @name: header name.
426 * Lookup the first transport header in @hash with a key equal to
429 * Return value: the header's value or %NULL if not found.
432 soup_message_get_header (GHashTable *hash, const char *name)
436 g_return_val_if_fail (hash != NULL, NULL);
437 g_return_val_if_fail (name != NULL || name [0] != '\0', NULL);
439 vals = g_hash_table_lookup (hash, name);
447 * soup_message_get_header_list:
448 * @hash: a header hash table
449 * @name: header name.
451 * Lookup the all transport request headers in @hash with a key equal
454 * Return value: a const pointer to a #GSList of header values or
455 * %NULL if not found.
458 soup_message_get_header_list (GHashTable *hash, const char *name)
460 g_return_val_if_fail (hash != NULL, NULL);
461 g_return_val_if_fail (name != NULL || name [0] != '\0', NULL);
463 return g_hash_table_lookup (hash, name);
469 } SoupMessageForeachHeaderData;
472 foreach_value_in_list (gpointer name, gpointer value, gpointer user_data)
474 GSList *vals = value;
475 SoupMessageForeachHeaderData *data = user_data;
478 (*data->func) (name, vals->data, data->user_data);
484 soup_message_foreach_header (GHashTable *hash, GHFunc func, gpointer user_data)
486 SoupMessageForeachHeaderData data;
488 g_return_if_fail (hash != NULL);
489 g_return_if_fail (func != NULL);
492 data.user_data = user_data;
493 g_hash_table_foreach (hash, foreach_value_in_list, &data);
497 * soup_message_prepare:
500 * Prepares @req to be sent, by cleaning up its prior response state
503 soup_message_prepare (SoupMessage *req)
505 soup_message_io_cancel (req);
507 if (req->status != SOUP_MESSAGE_STATUS_IDLE)
508 req->status = SOUP_MESSAGE_STATUS_IDLE;
510 if (req->response.owner == SOUP_BUFFER_SYSTEM_OWNED)
511 g_free (req->response.body);
513 req->response.owner = 0;
514 req->response.body = NULL;
515 req->response.length = 0;
519 soup_message_clear_headers (req->response_headers);
521 req->status_code = 0;
522 if (req->reason_phrase) {
523 g_free ((char *) req->reason_phrase);
524 req->reason_phrase = NULL;
529 soup_message_set_flags (SoupMessage *msg, guint flags)
531 g_return_if_fail (SOUP_IS_MESSAGE (msg));
533 msg->priv->msg_flags = flags;
537 soup_message_get_flags (SoupMessage *msg)
539 g_return_val_if_fail (SOUP_IS_MESSAGE (msg), 0);
541 return msg->priv->msg_flags;
545 soup_message_set_http_version (SoupMessage *msg, SoupHttpVersion version)
547 g_return_if_fail (SOUP_IS_MESSAGE (msg));
549 msg->priv->http_version = version;
553 soup_message_get_http_version (SoupMessage *msg)
555 g_return_val_if_fail (SOUP_IS_MESSAGE (msg), SOUP_HTTP_1_0);
557 return msg->priv->http_version;
561 soup_message_is_keepalive (SoupMessage *msg)
563 const char *c_conn, *s_conn;
565 c_conn = soup_message_get_header (msg->request_headers, "Connection");
566 s_conn = soup_message_get_header (msg->response_headers, "Connection");
568 if (msg->priv->http_version == SOUP_HTTP_1_0) {
569 /* Only persistent if the client requested keepalive
570 * and the server agreed.
573 if (!c_conn || !s_conn)
575 if (g_strcasecmp (c_conn, "Keep-Alive") != 0 ||
576 g_strcasecmp (s_conn, "Keep-Alive") != 0)
581 /* Persistent unless either side requested otherwise */
583 if (c_conn && g_strcasecmp (c_conn, "close") == 0)
585 if (s_conn && g_strcasecmp (s_conn, "close") == 0)
593 soup_message_set_uri (SoupMessage *msg, const SoupUri *new_uri)
595 g_return_if_fail (SOUP_IS_MESSAGE (msg));
597 if (msg->priv->uri && new_uri) {
598 if (strcmp (msg->priv->uri->host, new_uri->host) != 0)
599 soup_message_io_cancel (msg);
601 soup_message_io_cancel (msg);
604 soup_uri_free (msg->priv->uri);
605 msg->priv->uri = soup_uri_copy (new_uri);
609 soup_message_get_uri (SoupMessage *msg)
611 g_return_val_if_fail (SOUP_IS_MESSAGE (msg), NULL);
613 return msg->priv->uri;
617 soup_message_set_status (SoupMessage *msg, guint status_code)
619 g_return_if_fail (SOUP_IS_MESSAGE (msg));
620 g_return_if_fail (status_code != 0);
622 g_free ((char *) msg->reason_phrase);
624 msg->status_code = status_code;
625 msg->reason_phrase = g_strdup (soup_status_get_phrase (status_code));
629 soup_message_set_status_full (SoupMessage *msg,
631 const char *reason_phrase)
633 g_return_if_fail (SOUP_IS_MESSAGE (msg));
634 g_return_if_fail (status_code != 0);
635 g_return_if_fail (reason_phrase != NULL);
637 g_free ((char *) msg->reason_phrase);
639 msg->status_code = status_code;
640 msg->reason_phrase = g_strdup (reason_phrase);
645 soup_message_add_chunk (SoupMessage *msg,
650 SoupDataBuffer *chunk;
652 g_return_if_fail (SOUP_IS_MESSAGE (msg));
653 g_return_if_fail (body != NULL || length == 0);
655 chunk = g_new0 (SoupDataBuffer, 1);
656 if (owner == SOUP_BUFFER_USER_OWNED) {
657 chunk->owner = SOUP_BUFFER_SYSTEM_OWNED;
658 chunk->body = g_memdup (body, length);
660 chunk->owner = owner;
661 chunk->body = (char *)body;
663 chunk->length = length;
665 if (msg->priv->chunks) {
666 g_slist_append (msg->priv->last_chunk, chunk);
667 msg->priv->last_chunk = msg->priv->last_chunk->next;
669 msg->priv->chunks = msg->priv->last_chunk =
670 g_slist_append (NULL, chunk);
675 soup_message_add_final_chunk (SoupMessage *msg)
677 soup_message_add_chunk (msg, SOUP_BUFFER_STATIC, NULL, 0);
681 soup_message_pop_chunk (SoupMessage *msg)
683 SoupDataBuffer *chunk;
685 g_return_val_if_fail (SOUP_IS_MESSAGE (msg), NULL);
687 if (!msg->priv->chunks)
690 chunk = msg->priv->chunks->data;
691 msg->priv->chunks = g_slist_remove (msg->priv->chunks, chunk);
692 if (!msg->priv->chunks)
693 msg->priv->last_chunk = NULL;
699 free_chunks (SoupMessage *msg)
701 SoupDataBuffer *chunk;
704 for (ch = msg->priv->chunks; ch; ch = ch->next) {
707 if (chunk->owner == SOUP_BUFFER_SYSTEM_OWNED)
708 g_free (chunk->body);
712 g_slist_free (msg->priv->chunks);
713 msg->priv->chunks = msg->priv->last_chunk = NULL;