Add wrote_informational and got_informational signals.
[platform/upstream/libsoup.git] / libsoup / soup-message.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * soup-message.c: HTTP request/response
4  *
5  * Copyright (C) 2000-2003, Ximian, Inc.
6  */
7
8 #include <string.h>
9
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"
16
17 #define PARENT_TYPE G_TYPE_OBJECT
18 static GObjectClass *parent_class;
19
20 enum {
21         WROTE_INFORMATIONAL,
22         WROTE_HEADERS,
23         WROTE_CHUNK,
24         WROTE_BODY,
25
26         GOT_INFORMATIONAL,
27         GOT_HEADERS,
28         GOT_CHUNK,
29         GOT_BODY,
30
31         FINISHED,
32
33         LAST_SIGNAL
34 };
35
36 static guint signals[LAST_SIGNAL] = { 0 };
37
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);
43
44 static void
45 init (GObject *object)
46 {
47         SoupMessage *msg = SOUP_MESSAGE (object);
48
49         msg->priv = g_new0 (SoupMessagePrivate, 1);
50
51         msg->status  = SOUP_MESSAGE_STATUS_IDLE;
52
53         msg->request_headers = g_hash_table_new (soup_str_case_hash,
54                                                  soup_str_case_equal);
55
56         msg->response_headers = g_hash_table_new (soup_str_case_hash,
57                                                   soup_str_case_equal);
58
59         msg->priv->http_version = SOUP_HTTP_1_1;
60 }
61
62 static void
63 finalize (GObject *object)
64 {
65         SoupMessage *msg = SOUP_MESSAGE (object);
66
67         soup_message_io_cancel (msg);
68
69         if (msg->priv->uri)
70                 soup_uri_free (msg->priv->uri);
71
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);
76         free_chunks (msg);
77
78         soup_message_clear_headers (msg->request_headers);
79         g_hash_table_destroy (msg->request_headers);
80
81         soup_message_clear_headers (msg->response_headers);
82         g_hash_table_destroy (msg->response_headers);
83
84         g_slist_foreach (msg->priv->content_handlers, (GFunc) g_free, NULL);
85         g_slist_free (msg->priv->content_handlers);
86
87         g_free ((char *) msg->reason_phrase);
88
89         g_free (msg->priv);
90
91         G_OBJECT_CLASS (parent_class)->finalize (object);
92 }
93
94 static void
95 class_init (GObjectClass *object_class)
96 {
97         SoupMessageClass *message_class = SOUP_MESSAGE_CLASS (object_class);
98
99         parent_class = g_type_class_ref (PARENT_TYPE);
100
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;
106
107         /* virtual method override */
108         object_class->finalize = finalize;
109
110         /* signals */
111         signals[WROTE_INFORMATIONAL] =
112                 g_signal_new ("wrote_informational",
113                               G_OBJECT_CLASS_TYPE (object_class),
114                               G_SIGNAL_RUN_FIRST,
115                               G_STRUCT_OFFSET (SoupMessageClass, wrote_informational),
116                               NULL, NULL,
117                               soup_marshal_NONE__NONE,
118                               G_TYPE_NONE, 0);
119         signals[WROTE_HEADERS] =
120                 g_signal_new ("wrote_headers",
121                               G_OBJECT_CLASS_TYPE (object_class),
122                               G_SIGNAL_RUN_FIRST,
123                               G_STRUCT_OFFSET (SoupMessageClass, wrote_headers),
124                               NULL, NULL,
125                               soup_marshal_NONE__NONE,
126                               G_TYPE_NONE, 0);
127         signals[WROTE_CHUNK] =
128                 g_signal_new ("wrote_chunk",
129                               G_OBJECT_CLASS_TYPE (object_class),
130                               G_SIGNAL_RUN_FIRST,
131                               G_STRUCT_OFFSET (SoupMessageClass, wrote_chunk),
132                               NULL, NULL,
133                               soup_marshal_NONE__NONE,
134                               G_TYPE_NONE, 0);
135         signals[WROTE_BODY] =
136                 g_signal_new ("wrote_body",
137                               G_OBJECT_CLASS_TYPE (object_class),
138                               G_SIGNAL_RUN_FIRST,
139                               G_STRUCT_OFFSET (SoupMessageClass, wrote_body),
140                               NULL, NULL,
141                               soup_marshal_NONE__NONE,
142                               G_TYPE_NONE, 0);
143
144         signals[GOT_INFORMATIONAL] =
145                 g_signal_new ("got_informational",
146                               G_OBJECT_CLASS_TYPE (object_class),
147                               G_SIGNAL_RUN_FIRST,
148                               G_STRUCT_OFFSET (SoupMessageClass, got_informational),
149                               NULL, NULL,
150                               soup_marshal_NONE__NONE,
151                               G_TYPE_NONE, 0);
152         signals[GOT_HEADERS] =
153                 g_signal_new ("got_headers",
154                               G_OBJECT_CLASS_TYPE (object_class),
155                               G_SIGNAL_RUN_FIRST,
156                               G_STRUCT_OFFSET (SoupMessageClass, got_headers),
157                               NULL, NULL,
158                               soup_marshal_NONE__NONE,
159                               G_TYPE_NONE, 0);
160         signals[GOT_CHUNK] =
161                 g_signal_new ("got_chunk",
162                               G_OBJECT_CLASS_TYPE (object_class),
163                               G_SIGNAL_RUN_FIRST,
164                               G_STRUCT_OFFSET (SoupMessageClass, got_chunk),
165                               NULL, NULL,
166                               soup_marshal_NONE__NONE,
167                               G_TYPE_NONE, 0);
168         signals[GOT_BODY] =
169                 g_signal_new ("got_body",
170                               G_OBJECT_CLASS_TYPE (object_class),
171                               G_SIGNAL_RUN_FIRST,
172                               G_STRUCT_OFFSET (SoupMessageClass, got_body),
173                               NULL, NULL,
174                               soup_marshal_NONE__NONE,
175                               G_TYPE_NONE, 0);
176
177         signals[FINISHED] =
178                 g_signal_new ("finished",
179                               G_OBJECT_CLASS_TYPE (object_class),
180                               G_SIGNAL_RUN_FIRST,
181                               G_STRUCT_OFFSET (SoupMessageClass, finished),
182                               NULL, NULL,
183                               soup_marshal_NONE__NONE,
184                               G_TYPE_NONE, 0);
185 }
186
187 SOUP_MAKE_TYPE (soup_message, SoupMessage, class_init, init, PARENT_TYPE)
188
189
190 /**
191  * soup_message_new:
192  * @method: the HTTP method for the created request
193  * @uri_string: the destination endpoint (as a string)
194  * 
195  * Creates a new empty #SoupMessage, which will connect to @uri
196  *
197  * Return value: the new #SoupMessage (or %NULL if @uri could not
198  * be parsed).
199  */
200 SoupMessage *
201 soup_message_new (const char *method, const char *uri_string)
202 {
203         SoupMessage *msg;
204         SoupUri *uri;
205
206         uri = soup_uri_new (uri_string);
207         if (!uri)
208                 return NULL;
209
210         msg = g_object_new (SOUP_TYPE_MESSAGE, NULL);
211         msg->method = method ? method : SOUP_METHOD_GET;
212         msg->priv->uri = uri;
213
214         return msg;
215 }
216
217 /**
218  * soup_message_new_from_uri:
219  * @method: the HTTP method for the created request
220  * @uri: the destination endpoint (as a #SoupUri)
221  * 
222  * Creates a new empty #SoupMessage, which will connect to @uri
223  *
224  * Return value: the new #SoupMessage
225  */
226 SoupMessage *
227 soup_message_new_from_uri (const char *method, const SoupUri *uri)
228 {
229         SoupMessage *msg;
230
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);
234
235         return msg;
236 }
237
238 /**
239  * soup_message_set_request:
240  * @msg: the message
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.
245  * 
246  * Convenience function to set the request body of a #SoupMessage
247  */
248 void
249 soup_message_set_request (SoupMessage   *msg,
250                           const char    *content_type,
251                           SoupOwnership  req_owner,
252                           char          *req_body,
253                           gulong         req_length)
254 {
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);
258
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;
264 }
265
266 /**
267  * soup_message_set_response:
268  * @msg: the message
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.
273  * 
274  * Convenience function to set the response body of a #SoupMessage
275  */
276 void
277 soup_message_set_response (SoupMessage   *msg,
278                            const char    *content_type,
279                            SoupOwnership  resp_owner,
280                            char          *resp_body,
281                            gulong         resp_length)
282 {
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);
286
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;
292 }
293
294 void
295 soup_message_wrote_informational (SoupMessage *msg)
296 {
297         g_signal_emit (msg, signals[WROTE_INFORMATIONAL], 0);
298 }
299
300 void
301 soup_message_wrote_headers (SoupMessage *msg)
302 {
303         g_signal_emit (msg, signals[WROTE_HEADERS], 0);
304 }
305
306 void
307 soup_message_wrote_chunk (SoupMessage *msg)
308 {
309         g_signal_emit (msg, signals[WROTE_CHUNK], 0);
310 }
311
312 void
313 soup_message_wrote_body (SoupMessage *msg)
314 {
315         g_signal_emit (msg, signals[WROTE_BODY], 0);
316 }
317
318 void
319 soup_message_got_informational (SoupMessage *msg)
320 {
321         g_signal_emit (msg, signals[GOT_INFORMATIONAL], 0);
322 }
323
324 static void
325 got_headers (SoupMessage *req)
326 {
327         g_object_ref (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);
332 }
333
334 void
335 soup_message_got_headers (SoupMessage *msg)
336 {
337         g_signal_emit (msg, signals[GOT_HEADERS], 0);
338 }
339
340 static void
341 got_chunk (SoupMessage *req)
342 {
343         g_object_ref (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);
348 }
349
350 void
351 soup_message_got_chunk (SoupMessage *msg)
352 {
353         g_signal_emit (msg, signals[GOT_CHUNK], 0);
354 }
355
356 static void
357 got_body (SoupMessage *req)
358 {
359         g_object_ref (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);
364 }
365
366 void
367 soup_message_got_body (SoupMessage *msg)
368 {
369         g_signal_emit (msg, signals[GOT_BODY], 0);
370 }
371
372 static void
373 finished (SoupMessage *req)
374 {
375         soup_message_io_cancel (req);
376 }
377
378 void
379 soup_message_finished (SoupMessage *msg)
380 {
381         g_signal_emit (msg, signals[FINISHED], 0);
382 }
383
384
385 /**
386  * soup_message_cancel:
387  * @msg: a #SoupMessage currently being processed.
388  * 
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.
392  */
393 void
394 soup_message_cancel (SoupMessage *msg)
395 {
396         soup_message_set_status (msg, SOUP_STATUS_CANCELLED);
397         soup_message_finished (msg);
398 }
399
400 static gboolean
401 free_header_list (gpointer name, gpointer vals, gpointer user_data)
402 {
403         g_free (name);
404         g_slist_foreach (vals, (GFunc) g_free, NULL);
405         g_slist_free (vals);
406
407         return TRUE;
408 }
409
410 void
411 soup_message_clear_headers (GHashTable *hash)
412 {
413         g_return_if_fail (hash != NULL);
414
415         g_hash_table_foreach_remove (hash, free_header_list, NULL);
416 }
417
418 void
419 soup_message_remove_header (GHashTable *hash, const char *name)
420 {
421         gpointer old_key, old_vals;
422
423         g_return_if_fail (hash != NULL);
424         g_return_if_fail (name != NULL || name[0] != '\0');
425
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);
429         }
430 }
431
432 void
433 soup_message_add_header (GHashTable *hash, const char *name, const char *value)
434 {
435         GSList *old_value;
436
437         g_return_if_fail (hash != NULL);
438         g_return_if_fail (name != NULL || name [0] != '\0');
439         g_return_if_fail (value != NULL);
440
441         old_value = g_hash_table_lookup (hash, name);
442
443         if (old_value)
444                 g_slist_append (old_value, g_strdup (value));
445         else {
446                 g_hash_table_insert (hash, g_strdup (name),
447                                      g_slist_append (NULL, g_strdup (value)));
448         }
449 }
450
451 /**
452  * soup_message_get_header:
453  * @hash: a header hash table
454  * @name: header name.
455  * 
456  * Lookup the first transport header in @hash with a key equal to
457  * @name.
458  * 
459  * Return value: the header's value or %NULL if not found.
460  */
461 const char *
462 soup_message_get_header (GHashTable *hash, const char *name)
463 {
464         GSList *vals;
465
466         g_return_val_if_fail (hash != NULL, NULL);
467         g_return_val_if_fail (name != NULL || name [0] != '\0', NULL);
468
469         vals = g_hash_table_lookup (hash, name);
470         if (vals)
471                 return vals->data;
472
473         return NULL;
474 }
475
476 /**
477  * soup_message_get_header_list:
478  * @hash: a header hash table
479  * @name: header name.
480  * 
481  * Lookup the all transport request headers in @hash with a key equal
482  * to @name.
483  * 
484  * Return value: a const pointer to a #GSList of header values or
485  * %NULL if not found.
486  */
487 const GSList *
488 soup_message_get_header_list (GHashTable *hash, const char *name)
489 {
490         g_return_val_if_fail (hash != NULL, NULL);
491         g_return_val_if_fail (name != NULL || name [0] != '\0', NULL);
492
493         return g_hash_table_lookup (hash, name);
494 }
495
496 typedef struct {
497         GHFunc   func;
498         gpointer user_data;
499 } SoupMessageForeachHeaderData;
500
501 static void
502 foreach_value_in_list (gpointer name, gpointer value, gpointer user_data)
503 {
504         GSList *vals = value;
505         SoupMessageForeachHeaderData *data = user_data;
506
507         while (vals) {
508                 (*data->func) (name, vals->data, data->user_data);
509                 vals = vals->next;
510         }
511 }
512
513 void
514 soup_message_foreach_header (GHashTable *hash, GHFunc func, gpointer user_data)
515 {
516         SoupMessageForeachHeaderData data;
517
518         g_return_if_fail (hash != NULL);
519         g_return_if_fail (func != NULL);
520
521         data.func = func;
522         data.user_data = user_data;
523         g_hash_table_foreach (hash, foreach_value_in_list, &data);
524 }
525
526 /**
527  * soup_message_prepare:
528  * @req: a message
529  *
530  * Prepares @req to be sent, by cleaning up its prior response state
531  **/
532 void
533 soup_message_prepare (SoupMessage *req)
534 {
535         soup_message_io_cancel (req);
536
537         if (req->status != SOUP_MESSAGE_STATUS_IDLE)
538                 req->status = SOUP_MESSAGE_STATUS_IDLE;
539
540         if (req->response.owner == SOUP_BUFFER_SYSTEM_OWNED)
541                 g_free (req->response.body);
542
543         req->response.owner = 0;
544         req->response.body = NULL;
545         req->response.length = 0;
546
547         free_chunks (req);
548
549         soup_message_clear_headers (req->response_headers);
550
551         req->status_code = 0;
552         if (req->reason_phrase) {
553                 g_free ((char *) req->reason_phrase);
554                 req->reason_phrase = NULL;
555         }
556 }
557
558 void
559 soup_message_set_flags (SoupMessage *msg, guint flags)
560 {
561         g_return_if_fail (SOUP_IS_MESSAGE (msg));
562
563         msg->priv->msg_flags = flags;
564 }
565
566 guint
567 soup_message_get_flags (SoupMessage *msg)
568 {
569         g_return_val_if_fail (SOUP_IS_MESSAGE (msg), 0);
570
571         return msg->priv->msg_flags;
572 }
573
574 void
575 soup_message_set_http_version  (SoupMessage *msg, SoupHttpVersion version)
576 {
577         g_return_if_fail (SOUP_IS_MESSAGE (msg));
578
579         msg->priv->http_version = version;
580 }
581
582 SoupHttpVersion
583 soup_message_get_http_version (SoupMessage *msg)
584 {
585         g_return_val_if_fail (SOUP_IS_MESSAGE (msg), SOUP_HTTP_1_0);
586
587         return msg->priv->http_version;
588 }
589
590 gboolean
591 soup_message_is_keepalive (SoupMessage *msg)
592 {
593         const char *c_conn, *s_conn;
594
595         c_conn = soup_message_get_header (msg->request_headers, "Connection");
596         s_conn = soup_message_get_header (msg->response_headers, "Connection");
597
598         if (msg->priv->http_version == SOUP_HTTP_1_0) {
599                 /* Only persistent if the client requested keepalive
600                  * and the server agreed.
601                  */
602
603                 if (!c_conn || !s_conn)
604                         return FALSE;
605                 if (g_strcasecmp (c_conn, "Keep-Alive") != 0 ||
606                     g_strcasecmp (s_conn, "Keep-Alive") != 0)
607                         return FALSE;
608
609                 return TRUE;
610         } else {
611                 /* Persistent unless either side requested otherwise */
612
613                 if (c_conn && g_strcasecmp (c_conn, "close") == 0)
614                         return FALSE;
615                 if (s_conn && g_strcasecmp (s_conn, "close") == 0)
616                         return FALSE;
617
618                 return TRUE;
619         }
620 }
621
622 void
623 soup_message_set_uri (SoupMessage *msg, const SoupUri *new_uri)
624 {
625         g_return_if_fail (SOUP_IS_MESSAGE (msg));
626
627         if (msg->priv->uri && new_uri) {
628                 if (strcmp (msg->priv->uri->host, new_uri->host) != 0)
629                         soup_message_io_cancel (msg);
630         } else if (!new_uri)
631                 soup_message_io_cancel (msg);
632
633         if (msg->priv->uri)
634                 soup_uri_free (msg->priv->uri);
635         msg->priv->uri = soup_uri_copy (new_uri);
636 }
637
638 const SoupUri *
639 soup_message_get_uri (SoupMessage *msg)
640 {
641         g_return_val_if_fail (SOUP_IS_MESSAGE (msg), NULL);
642
643         return msg->priv->uri;
644 }
645
646 void
647 soup_message_set_status (SoupMessage *msg, guint status_code)
648 {
649         g_return_if_fail (SOUP_IS_MESSAGE (msg));
650         g_return_if_fail (status_code != 0);
651
652         g_free ((char *) msg->reason_phrase);
653
654         msg->status_code = status_code;
655         msg->reason_phrase = g_strdup (soup_status_get_phrase (status_code));
656 }
657
658 void
659 soup_message_set_status_full (SoupMessage *msg,
660                               guint        status_code,
661                               const char  *reason_phrase)
662 {
663         g_return_if_fail (SOUP_IS_MESSAGE (msg));
664         g_return_if_fail (status_code != 0);
665         g_return_if_fail (reason_phrase != NULL);
666
667         g_free ((char *) msg->reason_phrase);
668
669         msg->status_code = status_code;
670         msg->reason_phrase = g_strdup (reason_phrase);
671 }
672
673
674 void
675 soup_message_add_chunk (SoupMessage   *msg,
676                         SoupOwnership  owner,
677                         const char    *body,
678                         guint          length)
679 {
680         SoupDataBuffer *chunk;
681
682         g_return_if_fail (SOUP_IS_MESSAGE (msg));
683         g_return_if_fail (body != NULL || length == 0);
684
685         chunk = g_new0 (SoupDataBuffer, 1);
686         if (owner == SOUP_BUFFER_USER_OWNED) {
687                 chunk->owner = SOUP_BUFFER_SYSTEM_OWNED;
688                 chunk->body = g_memdup (body, length);
689         } else {
690                 chunk->owner = owner;
691                 chunk->body = (char *)body;
692         }
693         chunk->length = length;
694
695         if (msg->priv->chunks) {
696                 g_slist_append (msg->priv->last_chunk, chunk);
697                 msg->priv->last_chunk = msg->priv->last_chunk->next;
698         } else {
699                 msg->priv->chunks = msg->priv->last_chunk =
700                         g_slist_append (NULL, chunk);
701         }
702 }
703
704 void
705 soup_message_add_final_chunk (SoupMessage *msg)
706 {
707         soup_message_add_chunk (msg, SOUP_BUFFER_STATIC, NULL, 0);
708 }
709
710 SoupDataBuffer *
711 soup_message_pop_chunk (SoupMessage *msg)
712 {
713         SoupDataBuffer *chunk;
714
715         g_return_val_if_fail (SOUP_IS_MESSAGE (msg), NULL);
716
717         if (!msg->priv->chunks)
718                 return NULL;
719
720         chunk = msg->priv->chunks->data;
721         msg->priv->chunks = g_slist_remove (msg->priv->chunks, chunk);
722         if (!msg->priv->chunks)
723                 msg->priv->last_chunk = NULL;
724
725         return chunk;
726 }
727
728 static void
729 free_chunks (SoupMessage *msg)
730 {
731         SoupDataBuffer *chunk;
732         GSList *ch;
733
734         for (ch = msg->priv->chunks; ch; ch = ch->next) {
735                 chunk = ch->data;
736
737                 if (chunk->owner == SOUP_BUFFER_SYSTEM_OWNED)
738                         g_free (chunk->body);
739                 g_free (chunk);
740         }
741
742         g_slist_free (msg->priv->chunks);
743         msg->priv->chunks = msg->priv->last_chunk = NULL;
744 }