Define two new signals, request_queued and request_unqueued, to provided a
[platform/upstream/libsoup.git] / libsoup / soup-session.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * soup-session.c
4  *
5  * Copyright (C) 2000-2003, Ximian, Inc.
6  */
7
8 #ifdef HAVE_CONFIG_H
9 #include <config.h>
10 #endif
11
12 #include <unistd.h>
13 #include <string.h>
14 #include <stdlib.h>
15
16 #include "soup-auth.h"
17 #include "soup-auth-basic.h"
18 #include "soup-auth-digest.h"
19 #include "soup-auth-manager.h"
20 #include "soup-auth-manager-ntlm.h"
21 #include "soup-connection.h"
22 #include "soup-marshal.h"
23 #include "soup-message-private.h"
24 #include "soup-message-queue.h"
25 #include "soup-session.h"
26 #include "soup-session-private.h"
27 #include "soup-socket.h"
28 #include "soup-ssl.h"
29 #include "soup-uri.h"
30
31 /**
32  * SECTION:soup-session
33  * @short_description: Soup session state object
34  *
35  * #SoupSession is the object that controls client-side HTTP. A
36  * #SoupSession encapsulates all of the state that libsoup is keeping
37  * on behalf of your program; cached HTTP connections, authentication
38  * information, etc.
39  *
40  * Most applications will only need a single #SoupSession; the primary
41  * reason you might need multiple sessions is if you need to have
42  * multiple independent authentication contexts. (Eg, you are
43  * connecting to a server and authenticating as two different users at
44  * different times; the easiest way to ensure that each #SoupMessage
45  * is sent with the authentication information you intended is to use
46  * one session for the first user, and a second session for the other
47  * user.)
48  *
49  * #SoupSession itself is an abstract class, with two subclasses. If
50  * you are using the glib main loop, you will generally want to use
51  * #SoupSessionAsync, which uses non-blocking I/O and callbacks. On
52  * the other hand, if your application is threaded and you want to do
53  * synchronous I/O in a separate thread from the UI, use
54  * #SoupSessionSync.
55  **/
56
57 typedef struct {
58         SoupURI    *root_uri;
59
60         GSList     *connections;      /* CONTAINS: SoupConnection */
61         guint       num_conns;
62
63         GHashTable *auth_realms;      /* path -> scheme:realm */
64         GHashTable *auths;            /* scheme:realm -> SoupAuth */
65 } SoupSessionHost;
66
67 typedef struct {
68         SoupURI *proxy_uri;
69         SoupAuth *proxy_auth;
70
71         guint max_conns, max_conns_per_host;
72
73         char *ssl_ca_file;
74         SoupSSLCredentials *ssl_creds;
75
76         SoupMessageQueue *queue;
77
78         char *user_agent;
79
80         SoupAuthManager *auth_manager;
81         SoupAuthManagerNTLM *ntlm_manager;
82
83         GHashTable *hosts; /* SoupURI -> SoupSessionHost */
84         GHashTable *conns; /* SoupConnection -> SoupSessionHost */
85         guint num_conns;
86
87         /* Must hold the host_lock before potentially creating a
88          * new SoupSessionHost, or adding/removing a connection.
89          * Must not emit signals or destroy objects while holding it.
90          */
91         GMutex *host_lock;
92
93         GMainContext *async_context;
94
95         /* Holds the timeout value for the connection, when
96            no response is received.
97         */
98         guint timeout;
99 } SoupSessionPrivate;
100 #define SOUP_SESSION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_SESSION, SoupSessionPrivate))
101
102 static void     free_host      (SoupSessionHost *host);
103
104 static void queue_message   (SoupSession *session, SoupMessage *msg,
105                              SoupSessionCallback callback, gpointer user_data);
106 static void requeue_message (SoupSession *session, SoupMessage *msg);
107 static void cancel_message  (SoupSession *session, SoupMessage *msg,
108                              guint status_code);
109
110 /* temporary until we fix this to index hosts by SoupAddress */
111 extern guint     soup_uri_host_hash  (gconstpointer  key);
112 extern gboolean  soup_uri_host_equal (gconstpointer  v1,
113                                       gconstpointer  v2);
114 extern SoupURI  *soup_uri_copy_root  (SoupURI *uri);
115
116 #define SOUP_SESSION_MAX_CONNS_DEFAULT 10
117 #define SOUP_SESSION_MAX_CONNS_PER_HOST_DEFAULT 2
118
119 #define SOUP_SESSION_USER_AGENT_BASE "libsoup/" PACKAGE_VERSION
120
121 G_DEFINE_TYPE (SoupSession, soup_session, G_TYPE_OBJECT)
122
123 enum {
124         REQUEST_QUEUED,
125         REQUEST_STARTED,
126         REQUEST_UNQUEUED,
127         AUTHENTICATE,
128         LAST_SIGNAL
129 };
130
131 static guint signals[LAST_SIGNAL] = { 0 };
132
133 enum {
134         PROP_0,
135
136         PROP_PROXY_URI,
137         PROP_MAX_CONNS,
138         PROP_MAX_CONNS_PER_HOST,
139         PROP_USE_NTLM,
140         PROP_SSL_CA_FILE,
141         PROP_ASYNC_CONTEXT,
142         PROP_TIMEOUT,
143         PROP_USER_AGENT,
144
145         LAST_PROP
146 };
147
148 static void set_property (GObject *object, guint prop_id,
149                           const GValue *value, GParamSpec *pspec);
150 static void get_property (GObject *object, guint prop_id,
151                           GValue *value, GParamSpec *pspec);
152
153 static void
154 soup_session_init (SoupSession *session)
155 {
156         SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
157
158         priv->queue = soup_message_queue_new ();
159
160         priv->host_lock = g_mutex_new ();
161         priv->hosts = g_hash_table_new (soup_uri_host_hash,
162                                         soup_uri_host_equal);
163         priv->conns = g_hash_table_new (NULL, NULL);
164
165         priv->max_conns = SOUP_SESSION_MAX_CONNS_DEFAULT;
166         priv->max_conns_per_host = SOUP_SESSION_MAX_CONNS_PER_HOST_DEFAULT;
167
168         priv->timeout = 0;
169
170         priv->auth_manager = soup_auth_manager_new (session);
171         soup_auth_manager_add_type (priv->auth_manager, SOUP_TYPE_AUTH_BASIC);
172         soup_auth_manager_add_type (priv->auth_manager, SOUP_TYPE_AUTH_DIGEST);
173 }
174
175 static gboolean
176 foreach_free_host (gpointer key, gpointer host, gpointer data)
177 {
178         free_host (host);
179         return TRUE;
180 }
181
182 static void
183 cleanup_hosts (SoupSessionPrivate *priv)
184 {
185         GHashTable *old_hosts;
186
187         g_mutex_lock (priv->host_lock);
188         old_hosts = priv->hosts;
189         priv->hosts = g_hash_table_new (soup_uri_host_hash,
190                                         soup_uri_host_equal);
191         g_mutex_unlock (priv->host_lock);
192
193         g_hash_table_foreach_remove (old_hosts, foreach_free_host, NULL);
194         g_hash_table_destroy (old_hosts);
195 }
196
197 static void
198 dispose (GObject *object)
199 {
200         SoupSession *session = SOUP_SESSION (object);
201         SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
202
203         soup_session_abort (session);
204         cleanup_hosts (priv);
205
206         G_OBJECT_CLASS (soup_session_parent_class)->dispose (object);
207 }
208
209 static void
210 finalize (GObject *object)
211 {
212         SoupSession *session = SOUP_SESSION (object);
213         SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
214
215         soup_message_queue_destroy (priv->queue);
216
217         g_mutex_free (priv->host_lock);
218         g_hash_table_destroy (priv->hosts);
219         g_hash_table_destroy (priv->conns);
220
221         g_free (priv->user_agent);
222
223         soup_auth_manager_free (priv->auth_manager);
224         if (priv->ntlm_manager)
225                 soup_auth_manager_ntlm_free (priv->ntlm_manager);
226
227         if (priv->proxy_uri)
228                 soup_uri_free (priv->proxy_uri);
229
230         if (priv->ssl_creds)
231                 soup_ssl_free_client_credentials (priv->ssl_creds);
232
233         if (priv->async_context)
234                 g_main_context_unref (priv->async_context);
235
236         G_OBJECT_CLASS (soup_session_parent_class)->finalize (object);
237 }
238
239 static void
240 soup_session_class_init (SoupSessionClass *session_class)
241 {
242         GObjectClass *object_class = G_OBJECT_CLASS (session_class);
243
244         g_type_class_add_private (session_class, sizeof (SoupSessionPrivate));
245
246         /* virtual method definition */
247         session_class->queue_message = queue_message;
248         session_class->requeue_message = requeue_message;
249         session_class->cancel_message = cancel_message;
250
251         /* virtual method override */
252         object_class->dispose = dispose;
253         object_class->finalize = finalize;
254         object_class->set_property = set_property;
255         object_class->get_property = get_property;
256
257         /* signals */
258
259         /**
260          * SoupSession::request-queued:
261          * @session: the session
262          * @msg: the request that was queued
263          *
264          * Emitted when a request is queued on @session. (Note that
265          * "queued" doesn't just mean soup_session_queue_message();
266          * soup_session_send_message() implicitly queues the message
267          * as well.)
268          *
269          * When sending a request, first #SoupSession::request_queued
270          * is emitted, indicating that the session has become aware of
271          * the request.
272          *
273          * Once a connection is available to send the request on, the
274          * session emits #SoupSession::request_started. Then, various
275          * #SoupMessage signals are emitted as the message is
276          * processed. If the message is requeued, it will emit
277          * #SoupMessage::restarted, which will then be followed by
278          * another #SoupSession::request_started and another set of
279          * #SoupMessage signals when the message is re-sent.
280          *
281          * Eventually, the message will emit #SoupMessage::finished.
282          * Normally, this signals the completion of message
283          * processing. However, it is possible that the application
284          * will requeue the message from the "finished" handler (or
285          * equivalently, from the soup_session_queue_message()
286          * callback). In that case, the process will loop back to
287          * #SoupSession::request_started.
288          *
289          * Eventually, a message will reach "finished" and not be
290          * requeued. At that point, the session will emit
291          * #SoupSession::request_unqueued to indicate that it is done
292          * with the message.
293          *
294          * To sum up: #SoupSession::request_queued and
295          * #SoupSession::request_unqueued are guaranteed to be emitted
296          * exactly once, but #SoupSession::request_started and
297          * #SoupMessage::finished (and all of the other #SoupMessage
298          * signals) may be invoked multiple times for a given message.
299          **/
300         signals[REQUEST_QUEUED] =
301                 g_signal_new ("request-queued",
302                               G_OBJECT_CLASS_TYPE (object_class),
303                               G_SIGNAL_RUN_FIRST,
304                               0, /* FIXME? */
305                               NULL, NULL,
306                               soup_marshal_NONE__OBJECT,
307                               G_TYPE_NONE, 1,
308                               SOUP_TYPE_MESSAGE);
309
310         /**
311          * SoupSession::request-started:
312          * @session: the session
313          * @msg: the request being sent
314          * @socket: the socket the request is being sent on
315          *
316          * Emitted just before a request is sent. See
317          * #SoupSession::request_queued for a detailed description of
318          * the message lifecycle within a session.
319          **/
320         signals[REQUEST_STARTED] =
321                 g_signal_new ("request-started",
322                               G_OBJECT_CLASS_TYPE (object_class),
323                               G_SIGNAL_RUN_FIRST,
324                               G_STRUCT_OFFSET (SoupSessionClass, request_started),
325                               NULL, NULL,
326                               soup_marshal_NONE__OBJECT_OBJECT,
327                               G_TYPE_NONE, 2,
328                               SOUP_TYPE_MESSAGE,
329                               SOUP_TYPE_SOCKET);
330
331         /**
332          * SoupSession::request-unqueued:
333          * @session: the session
334          * @msg: the request that was unqueued
335          *
336          * Emitted when a request is removed from @session's queue,
337          * indicating that @session is done with it. See
338          * #SoupSession::request_queued for a detailed description of the
339          * message lifecycle within a session.
340          **/
341         signals[REQUEST_UNQUEUED] =
342                 g_signal_new ("request-unqueued",
343                               G_OBJECT_CLASS_TYPE (object_class),
344                               G_SIGNAL_RUN_FIRST,
345                               0, /* FIXME? */
346                               NULL, NULL,
347                               soup_marshal_NONE__OBJECT,
348                               G_TYPE_NONE, 1,
349                               SOUP_TYPE_MESSAGE);
350
351         /**
352          * SoupSession::authenticate:
353          * @session: the session
354          * @msg: the #SoupMessage being sent
355          * @auth: the #SoupAuth to authenticate
356          * @retrying: %TRUE if this is the second (or later) attempt
357          *
358          * Emitted when the session requires authentication. If
359          * credentials are available call soup_auth_authenticate() on
360          * @auth. If these credentials fail, the signal will be
361          * emitted again, with @retrying set to %TRUE, which will
362          * continue until you return without calling
363          * soup_auth_authenticate() on @auth.
364          *
365          * Note that this may be emitted before @msg's body has been
366          * fully read.
367          *
368          * If you call soup_session_pause_message() on @msg before
369          * returning, then you can authenticate @auth asynchronously
370          * (as long as you g_object_ref() it to make sure it doesn't
371          * get destroyed), and then unpause @msg when you are ready
372          * for it to continue.
373          **/
374         signals[AUTHENTICATE] =
375                 g_signal_new ("authenticate",
376                               G_OBJECT_CLASS_TYPE (object_class),
377                               G_SIGNAL_RUN_FIRST,
378                               G_STRUCT_OFFSET (SoupSessionClass, authenticate),
379                               NULL, NULL,
380                               soup_marshal_NONE__OBJECT_OBJECT_BOOLEAN,
381                               G_TYPE_NONE, 3,
382                               SOUP_TYPE_MESSAGE,
383                               SOUP_TYPE_AUTH,
384                               G_TYPE_BOOLEAN);
385
386         /* properties */
387         g_object_class_install_property (
388                 object_class, PROP_PROXY_URI,
389                 g_param_spec_boxed (SOUP_SESSION_PROXY_URI,
390                                     "Proxy URI",
391                                     "The HTTP Proxy to use for this session",
392                                     SOUP_TYPE_URI,
393                                     G_PARAM_READWRITE));
394         g_object_class_install_property (
395                 object_class, PROP_MAX_CONNS,
396                 g_param_spec_int (SOUP_SESSION_MAX_CONNS,
397                                   "Max Connection Count",
398                                   "The maximum number of connections that the session can open at once",
399                                   1,
400                                   G_MAXINT,
401                                   SOUP_SESSION_MAX_CONNS_DEFAULT,
402                                   G_PARAM_READWRITE));
403         g_object_class_install_property (
404                 object_class, PROP_MAX_CONNS_PER_HOST,
405                 g_param_spec_int (SOUP_SESSION_MAX_CONNS_PER_HOST,
406                                   "Max Per-Host Connection Count",
407                                   "The maximum number of connections that the session can open at once to a given host",
408                                   1,
409                                   G_MAXINT,
410                                   SOUP_SESSION_MAX_CONNS_PER_HOST_DEFAULT,
411                                   G_PARAM_READWRITE));
412         g_object_class_install_property (
413                 object_class, PROP_USE_NTLM,
414                 g_param_spec_boolean (SOUP_SESSION_USE_NTLM,
415                                       "Use NTLM",
416                                       "Whether or not to use NTLM authentication",
417                                       FALSE,
418                                       G_PARAM_READWRITE));
419         g_object_class_install_property (
420                 object_class, PROP_SSL_CA_FILE,
421                 g_param_spec_string (SOUP_SESSION_SSL_CA_FILE,
422                                      "SSL CA file",
423                                      "File containing SSL CA certificates",
424                                      NULL,
425                                      G_PARAM_READWRITE));
426         g_object_class_install_property (
427                 object_class, PROP_ASYNC_CONTEXT,
428                 g_param_spec_pointer (SOUP_SESSION_ASYNC_CONTEXT,
429                                       "Async GMainContext",
430                                       "The GMainContext to dispatch async I/O in",
431                                       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
432         g_object_class_install_property (
433                 object_class, PROP_TIMEOUT,
434                 g_param_spec_uint (SOUP_SESSION_TIMEOUT,
435                                    "Timeout value",
436                                    "Value in seconds to timeout a blocking I/O",
437                                    0, G_MAXUINT, 0,
438                                    G_PARAM_READWRITE));
439
440         /**
441          * SoupSession:user-agent:
442          *
443          * If non-%NULL, the value to use for the "User-Agent" header
444          * on #SoupMessage<!-- -->s sent from this session.
445          *
446          * RFC 2616 says: "The User-Agent request-header field
447          * contains information about the user agent originating the
448          * request. This is for statistical purposes, the tracing of
449          * protocol violations, and automated recognition of user
450          * agents for the sake of tailoring responses to avoid
451          * particular user agent limitations. User agents SHOULD
452          * include this field with requests."
453          *
454          * The User-Agent header contains a list of one or more
455          * product tokens, separated by whitespace, with the most
456          * significant product token coming first. The tokens must be
457          * brief, ASCII, and mostly alphanumeric (although "-", "_",
458          * and "." are also allowed), and may optionally include a "/"
459          * followed by a version string. You may also put comments,
460          * enclosed in parentheses, between or after the tokens.
461          *
462          * If you set a %user_agent property that has trailing
463          * whitespace, #SoupSession will append its own product token
464          * (eg, "<literal>libsoup/2.3.2</literal>") to the end of the
465          * header for you.
466          **/
467         g_object_class_install_property (
468                 object_class, PROP_USER_AGENT,
469                 g_param_spec_string (SOUP_SESSION_USER_AGENT,
470                                      "User-Agent string",
471                                      "User-Agent string",
472                                      NULL,
473                                      G_PARAM_READWRITE));
474 }
475
476 static gboolean
477 safe_uri_equal (SoupURI *a, SoupURI *b)
478 {
479         if (!a && !b)
480                 return TRUE;
481
482         if ((a && !b) || (b && !a))
483                 return FALSE;
484
485         return soup_uri_equal (a, b);
486 }
487
488 static gboolean
489 safe_str_equal (const char *a, const char *b)
490 {
491         if (!a && !b)
492                 return TRUE;
493
494         if ((a && !b) || (b && !a))
495                 return FALSE;
496
497         return strcmp (a, b) == 0;
498 }
499
500 static void
501 set_property (GObject *object, guint prop_id,
502               const GValue *value, GParamSpec *pspec)
503 {
504         SoupSession *session = SOUP_SESSION (object);
505         SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
506         SoupURI *uri;
507         gboolean need_abort = FALSE;
508         gboolean ca_file_changed = FALSE;
509         const char *new_ca_file, *user_agent;
510
511         switch (prop_id) {
512         case PROP_PROXY_URI:
513                 uri = g_value_get_boxed (value);
514
515                 if (!safe_uri_equal (priv->proxy_uri, uri))
516                         need_abort = TRUE;
517
518                 if (priv->proxy_uri)
519                         soup_uri_free (priv->proxy_uri);
520
521                 priv->proxy_uri = uri ? soup_uri_copy (uri) : NULL;
522
523                 if (need_abort) {
524                         soup_session_abort (session);
525                         cleanup_hosts (priv);
526                 }
527
528                 break;
529         case PROP_MAX_CONNS:
530                 priv->max_conns = g_value_get_int (value);
531                 break;
532         case PROP_MAX_CONNS_PER_HOST:
533                 priv->max_conns_per_host = g_value_get_int (value);
534                 break;
535         case PROP_USE_NTLM:
536                 if (g_value_get_boolean (value)) {
537                         if (!priv->ntlm_manager)
538                                 priv->ntlm_manager = soup_auth_manager_ntlm_new (session);
539                 } else {
540                         if (priv->ntlm_manager) {
541                                 soup_auth_manager_ntlm_free (priv->ntlm_manager);
542                                 priv->ntlm_manager = NULL;
543                         }
544                 }
545                 break;
546         case PROP_SSL_CA_FILE:
547                 new_ca_file = g_value_get_string (value);
548
549                 if (!safe_str_equal (priv->ssl_ca_file, new_ca_file))
550                         ca_file_changed = TRUE;
551
552                 g_free (priv->ssl_ca_file);
553                 priv->ssl_ca_file = g_strdup (new_ca_file);
554
555                 if (ca_file_changed) {
556                         if (priv->ssl_creds) {
557                                 soup_ssl_free_client_credentials (priv->ssl_creds);
558                                 priv->ssl_creds = NULL;
559                         }
560
561                         cleanup_hosts (priv);
562                 }
563
564                 break;
565         case PROP_ASYNC_CONTEXT:
566                 priv->async_context = g_value_get_pointer (value);
567                 if (priv->async_context)
568                         g_main_context_ref (priv->async_context);
569                 break;
570         case PROP_TIMEOUT:
571                 priv->timeout = g_value_get_uint (value);
572                 break;
573         case PROP_USER_AGENT:
574                 g_free (priv->user_agent);
575                 user_agent = g_value_get_string (value);
576                 if (!user_agent)
577                         priv->user_agent = NULL;
578                 else if (!*user_agent) {
579                         priv->user_agent =
580                                 g_strdup (SOUP_SESSION_USER_AGENT_BASE);
581                 } else if (g_str_has_suffix (user_agent, " ")) {
582                         priv->user_agent =
583                                 g_strdup_printf ("%s%s", user_agent,
584                                                  SOUP_SESSION_USER_AGENT_BASE);
585                 } else
586                         priv->user_agent = g_strdup (user_agent);
587                 break;
588         default:
589                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
590                 break;
591         }
592 }
593
594 static void
595 get_property (GObject *object, guint prop_id,
596               GValue *value, GParamSpec *pspec)
597 {
598         SoupSession *session = SOUP_SESSION (object);
599         SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
600
601         switch (prop_id) {
602         case PROP_PROXY_URI:
603                 g_value_set_boxed (value, priv->proxy_uri);
604                 break;
605         case PROP_MAX_CONNS:
606                 g_value_set_int (value, priv->max_conns);
607                 break;
608         case PROP_MAX_CONNS_PER_HOST:
609                 g_value_set_int (value, priv->max_conns_per_host);
610                 break;
611         case PROP_USE_NTLM:
612                 g_value_set_boolean (value, priv->ntlm_manager != NULL);
613                 break;
614         case PROP_SSL_CA_FILE:
615                 g_value_set_string (value, priv->ssl_ca_file);
616                 break;
617         case PROP_ASYNC_CONTEXT:
618                 g_value_set_pointer (value, priv->async_context ? g_main_context_ref (priv->async_context) : NULL);
619                 break;
620         case PROP_TIMEOUT:
621                 g_value_set_uint (value, priv->timeout);
622                 break;
623         case PROP_USER_AGENT:
624                 g_value_set_string (value, priv->user_agent);
625                 break;
626         default:
627                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
628                 break;
629         }
630 }
631
632
633 /**
634  * soup_session_get_async_context:
635  * @session: a #SoupSession
636  *
637  * Gets @session's async_context. This does not add a ref to the
638  * context, so you will need to ref it yourself if you want it to
639  * outlive its session.
640  *
641  * Return value: @session's #GMainContext, which may be %NULL
642  **/
643 GMainContext *
644 soup_session_get_async_context (SoupSession *session)
645 {
646         SoupSessionPrivate *priv;
647
648         g_return_val_if_fail (SOUP_IS_SESSION (session), NULL);
649         priv = SOUP_SESSION_GET_PRIVATE (session);
650
651         return priv->async_context;
652 }
653
654 /* Hosts */
655
656 static SoupSessionHost *
657 soup_session_host_new (SoupSession *session, SoupURI *source_uri)
658 {
659         SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
660         SoupSessionHost *host;
661
662         host = g_slice_new0 (SoupSessionHost);
663         host->root_uri = soup_uri_copy_root (source_uri);
664
665         if (host->root_uri->scheme == SOUP_URI_SCHEME_HTTPS &&
666             !priv->ssl_creds) {
667                 priv->ssl_creds =
668                         soup_ssl_get_client_credentials (priv->ssl_ca_file);
669         }
670
671         return host;
672 }
673
674 /* Note: get_host_for_message doesn't lock the host_lock. The caller
675  * must do it itself if there's a chance the host doesn't already
676  * exist.
677  */
678 static SoupSessionHost *
679 get_host_for_message (SoupSession *session, SoupMessage *msg)
680 {
681         SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
682         SoupSessionHost *host;
683         SoupURI *source = soup_message_get_uri (msg);
684
685         host = g_hash_table_lookup (priv->hosts, source);
686         if (host)
687                 return host;
688
689         host = soup_session_host_new (session, source);
690         g_hash_table_insert (priv->hosts, host->root_uri, host);
691
692         return host;
693 }
694
695 static void
696 free_host (SoupSessionHost *host)
697 {
698         while (host->connections) {
699                 SoupConnection *conn = host->connections->data;
700
701                 host->connections = g_slist_remove (host->connections, conn);
702                 soup_connection_disconnect (conn);
703         }
704
705         soup_uri_free (host->root_uri);
706         g_slice_free (SoupSessionHost, host);
707 }       
708
709 void
710 soup_session_emit_authenticate (SoupSession *session, SoupMessage *msg,
711                                 SoupAuth *auth, gboolean retrying)
712 {
713         g_signal_emit (session, signals[AUTHENTICATE], 0, msg, auth, retrying);
714 }
715
716 static void
717 redirect_handler (SoupMessage *msg, gpointer user_data)
718 {
719         SoupSession *session = user_data;
720         const char *new_loc;
721         SoupURI *new_uri;
722
723         new_loc = soup_message_headers_get (msg->response_headers, "Location");
724         g_return_if_fail (new_loc != NULL);
725
726         if (msg->status_code == SOUP_STATUS_MOVED_PERMANENTLY ||
727             msg->status_code == SOUP_STATUS_TEMPORARY_REDIRECT) {
728                 /* Don't redirect non-safe methods */
729                 if (msg->method != SOUP_METHOD_GET &&
730                     msg->method != SOUP_METHOD_HEAD &&
731                     msg->method != SOUP_METHOD_OPTIONS &&
732                     msg->method != SOUP_METHOD_PROPFIND)
733                         return;
734         } else if (msg->status_code == SOUP_STATUS_SEE_OTHER ||
735                    msg->status_code == SOUP_STATUS_FOUND) {
736                 /* Redirect using a GET */
737                 g_object_set (msg,
738                               SOUP_MESSAGE_METHOD, SOUP_METHOD_GET,
739                               NULL);
740                 soup_message_set_request (msg, NULL,
741                                           SOUP_MEMORY_STATIC, NULL, 0);
742                 soup_message_headers_set_encoding (msg->request_headers,
743                                                    SOUP_ENCODING_NONE);
744         } else {
745                 /* Three possibilities:
746                  *
747                  *   1) This was a non-3xx response that happened to
748                  *      have a "Location" header
749                  *   2) It's a non-redirecty 3xx response (300, 304,
750                  *      305, 306)
751                  *   3) It's some newly-defined 3xx response (308+)
752                  *
753                  * We ignore all of these cases. In the first two,
754                  * redirecting would be explicitly wrong, and in the
755                  * last case, we have no clue if the 3xx response is
756                  * supposed to be redirecty or non-redirecty. Plus,
757                  * 2616 says unrecognized status codes should be
758                  * treated as the equivalent to the x00 code, and we
759                  * don't redirect on 300, so therefore we shouldn't
760                  * redirect on 308+ either.
761                  */
762                 return;
763         }
764
765         /* Location is supposed to be an absolute URI, but some sites
766          * are lame, so we use soup_uri_new_with_base().
767          */
768         new_uri = soup_uri_new_with_base (soup_message_get_uri (msg), new_loc);
769         if (!new_uri) {
770                 soup_message_set_status_full (msg,
771                                               SOUP_STATUS_MALFORMED,
772                                               "Invalid Redirect URL");
773                 return;
774         }
775
776         soup_message_set_uri (msg, new_uri);
777         soup_uri_free (new_uri);
778
779         soup_session_requeue_message (session, msg);
780 }
781
782 static void
783 connection_started_request (SoupConnection *conn, SoupMessage *msg,
784                             gpointer data)
785 {
786         SoupSession *session = data;
787         SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
788
789         if (priv->user_agent) {
790                 soup_message_headers_replace (msg->request_headers,
791                                               "User-Agent", priv->user_agent);
792         }
793
794         /* Kludge to deal with the fact that CONNECT msgs come from the
795          * SoupConnection rather than being queued normally.
796          */
797         if (msg->method == SOUP_METHOD_CONNECT)
798                 g_signal_emit (session, signals[REQUEST_QUEUED], 0, msg);
799
800         g_signal_emit (session, signals[REQUEST_STARTED], 0,
801                        msg, soup_connection_get_socket (conn));
802 }
803
804 static void
805 find_oldest_connection (gpointer key, gpointer host, gpointer data)
806 {
807         SoupConnection *conn = key, **oldest = data;
808
809         /* Don't prune a connection that is currently in use, or
810          * hasn't been used yet.
811          */
812         if (soup_connection_is_in_use (conn) ||
813             soup_connection_last_used (conn) == 0)
814                 return;
815
816         if (!*oldest || (soup_connection_last_used (conn) <
817                          soup_connection_last_used (*oldest)))
818                 *oldest = conn;
819 }
820
821 /**
822  * soup_session_try_prune_connection:
823  * @session: a #SoupSession
824  *
825  * Finds the least-recently-used idle connection in @session and closes
826  * it.
827  *
828  * Return value: %TRUE if a connection was closed, %FALSE if there are
829  * no idle connections.
830  **/
831 gboolean
832 soup_session_try_prune_connection (SoupSession *session)
833 {
834         SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
835         SoupConnection *oldest = NULL;
836
837         g_mutex_lock (priv->host_lock);
838         g_hash_table_foreach (priv->conns, find_oldest_connection,
839                               &oldest);
840         if (oldest) {
841                 /* Ref the connection before unlocking the mutex in
842                  * case someone else tries to prune it too.
843                  */
844                 g_object_ref (oldest);
845                 g_mutex_unlock (priv->host_lock);
846                 soup_connection_disconnect (oldest);
847                 g_object_unref (oldest);
848                 return TRUE;
849         } else {
850                 g_mutex_unlock (priv->host_lock);
851                 return FALSE;
852         }
853 }
854
855 static void
856 connection_disconnected (SoupConnection *conn, gpointer user_data)
857 {
858         SoupSession *session = user_data;
859         SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
860         SoupSessionHost *host;
861
862         g_mutex_lock (priv->host_lock);
863
864         host = g_hash_table_lookup (priv->conns, conn);
865         if (host) {
866                 g_hash_table_remove (priv->conns, conn);
867                 host->connections = g_slist_remove (host->connections, conn);
868                 host->num_conns--;
869         }
870
871         g_signal_handlers_disconnect_by_func (conn, connection_disconnected, session);
872         priv->num_conns--;
873
874         g_mutex_unlock (priv->host_lock);
875         g_object_unref (conn);
876 }
877
878 static void
879 connect_result (SoupConnection *conn, guint status, gpointer user_data)
880 {
881         SoupSession *session = user_data;
882         SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
883         SoupSessionHost *host;
884         SoupMessageQueueIter iter;
885         SoupMessage *msg;
886
887         g_mutex_lock (priv->host_lock);
888
889         host = g_hash_table_lookup (priv->conns, conn);
890         if (!host) {
891                 g_mutex_unlock (priv->host_lock);
892                 return;
893         }
894
895         if (status == SOUP_STATUS_OK) {
896                 soup_connection_reserve (conn);
897                 host->connections = g_slist_prepend (host->connections, conn);
898                 g_mutex_unlock (priv->host_lock);
899                 return;
900         }
901
902         /* The connection failed. */
903         g_mutex_unlock (priv->host_lock);
904         connection_disconnected (conn, session);
905
906         if (host->connections) {
907                 /* Something went wrong this time, but we have at
908                  * least one open connection to this host. So just
909                  * leave the message in the queue so it can use that
910                  * connection once it's free.
911                  */
912                 return;
913         }
914
915         /* There are two possibilities: either status is
916          * SOUP_STATUS_TRY_AGAIN, in which case the session implementation
917          * will create a new connection (and all we need to do here
918          * is downgrade the message from CONNECTING to QUEUED); or
919          * status is something else, probably CANT_CONNECT or
920          * CANT_RESOLVE or the like, in which case we need to cancel
921          * any messages waiting for this host, since they're out
922          * of luck.
923          */
924         for (msg = soup_message_queue_first (priv->queue, &iter); msg; msg = soup_message_queue_next (priv->queue, &iter)) {
925                 if (get_host_for_message (session, msg) == host) {
926                         if (status == SOUP_STATUS_TRY_AGAIN) {
927                                 if (soup_message_get_io_status (msg) == SOUP_MESSAGE_IO_STATUS_CONNECTING)
928                                         soup_message_set_io_status (msg, SOUP_MESSAGE_IO_STATUS_QUEUED);
929                         } else {
930                                 soup_session_cancel_message (session, msg,
931                                                              status);
932                         }
933                 }
934         }
935 }
936
937 /**
938  * soup_session_get_connection:
939  * @session: a #SoupSession
940  * @msg: a #SoupMessage
941  * @try_pruning: on return, whether or not to try pruning a connection
942  * @is_new: on return, %TRUE if the returned connection is new and not
943  * yet connected
944  * 
945  * Tries to find or create a connection for @msg; this is an internal
946  * method for #SoupSession subclasses.
947  *
948  * If there is an idle connection to the relevant host available, then
949  * that connection will be returned (with *@is_new set to %FALSE). The
950  * connection will be marked "reserved", so the caller must call
951  * soup_connection_release() if it ends up not using the connection
952  * right away.
953  *
954  * If there is no idle connection available, but it is possible to
955  * create a new connection, then one will be created and returned,
956  * with *@is_new set to %TRUE. The caller MUST then call
957  * soup_connection_connect_sync() or soup_connection_connect_async()
958  * to connect it. If the connection attempt succeeds, the connection
959  * will be marked "reserved" and added to @session's connection pool
960  * once it connects. If the connection attempt fails, the connection
961  * will be unreffed.
962  *
963  * If no connection is available and a new connection cannot be made,
964  * soup_session_get_connection() will return %NULL. If @session has
965  * the maximum number of open connections open, but does not have the
966  * maximum number of per-host connections open to the relevant host,
967  * then *@try_pruning will be set to %TRUE. In this case, the caller
968  * can call soup_session_try_prune_connection() to close an idle
969  * connection, and then try soup_session_get_connection() again. (If
970  * calling soup_session_try_prune_connection() wouldn't help, then
971  * *@try_pruning is left untouched; it is NOT set to %FALSE.)
972  *
973  * Return value: a #SoupConnection, or %NULL
974  **/
975 SoupConnection *
976 soup_session_get_connection (SoupSession *session, SoupMessage *msg,
977                              gboolean *try_pruning, gboolean *is_new)
978 {
979         SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
980         SoupConnection *conn;
981         SoupSessionHost *host;
982         GSList *conns;
983
984         g_mutex_lock (priv->host_lock);
985
986         host = get_host_for_message (session, msg);
987         for (conns = host->connections; conns; conns = conns->next) {
988                 if (!soup_connection_is_in_use (conns->data)) {
989                         soup_connection_reserve (conns->data);
990                         g_mutex_unlock (priv->host_lock);
991                         *is_new = FALSE;
992                         return conns->data;
993                 }
994         }
995
996         if (soup_message_get_io_status (msg) == SOUP_MESSAGE_IO_STATUS_CONNECTING) {
997                 /* We already started a connection for this
998                  * message, so don't start another one.
999                  */
1000                 g_mutex_unlock (priv->host_lock);
1001                 return NULL;
1002         }
1003
1004         if (host->num_conns >= priv->max_conns_per_host) {
1005                 g_mutex_unlock (priv->host_lock);
1006                 return NULL;
1007         }
1008
1009         if (priv->num_conns >= priv->max_conns) {
1010                 *try_pruning = TRUE;
1011                 g_mutex_unlock (priv->host_lock);
1012                 return NULL;
1013         }
1014
1015         conn = soup_connection_new (
1016                 SOUP_CONNECTION_ORIGIN_URI, host->root_uri,
1017                 SOUP_CONNECTION_PROXY_URI, priv->proxy_uri,
1018                 SOUP_CONNECTION_SSL_CREDENTIALS, priv->ssl_creds,
1019                 SOUP_CONNECTION_ASYNC_CONTEXT, priv->async_context,
1020                 SOUP_CONNECTION_TIMEOUT, priv->timeout,
1021                 NULL);
1022         g_signal_connect (conn, "connect_result",
1023                           G_CALLBACK (connect_result),
1024                           session);
1025         g_signal_connect (conn, "disconnected",
1026                           G_CALLBACK (connection_disconnected),
1027                           session);
1028         g_signal_connect (conn, "request_started",
1029                           G_CALLBACK (connection_started_request),
1030                           session);
1031
1032         g_hash_table_insert (priv->conns, conn, host);
1033
1034         /* We increment the connection counts so it counts against the
1035          * totals, but we don't add it to the host's connection list
1036          * yet, since it's not ready for use.
1037          */
1038         priv->num_conns++;
1039         host->num_conns++;
1040
1041         /* Mark the request as connecting, so we don't try to open
1042          * another new connection for it while waiting for this one.
1043          */
1044         soup_message_set_io_status (msg, SOUP_MESSAGE_IO_STATUS_CONNECTING);
1045
1046         g_mutex_unlock (priv->host_lock);
1047         *is_new = TRUE;
1048         return conn;
1049 }
1050
1051 SoupMessageQueue *
1052 soup_session_get_queue (SoupSession *session)
1053 {
1054         SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1055
1056         return priv->queue;
1057 }
1058
1059 static void
1060 message_finished (SoupMessage *msg, gpointer user_data)
1061 {
1062         SoupSession *session = user_data;
1063         SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1064
1065         if (!SOUP_MESSAGE_IS_STARTING (msg)) {
1066                 soup_message_queue_remove_message (priv->queue, msg);
1067                 g_signal_handlers_disconnect_by_func (msg, message_finished, session);
1068                 g_signal_handlers_disconnect_by_func (msg, redirect_handler, session);
1069                 g_signal_emit (session, signals[REQUEST_UNQUEUED], 0, msg);
1070         }
1071 }
1072
1073 static void
1074 queue_message (SoupSession *session, SoupMessage *msg,
1075                SoupSessionCallback callback, gpointer user_data)
1076 {
1077         SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1078
1079         g_signal_connect_after (msg, "finished",
1080                                 G_CALLBACK (message_finished), session);
1081
1082         if (!(soup_message_get_flags (msg) & SOUP_MESSAGE_NO_REDIRECT)) {
1083                 soup_message_add_header_handler (
1084                         msg, "got_body", "Location",
1085                         G_CALLBACK (redirect_handler), session);
1086         }
1087
1088         soup_message_set_io_status (msg, SOUP_MESSAGE_IO_STATUS_QUEUED);
1089         soup_message_queue_append (priv->queue, msg);
1090
1091         g_signal_emit (session, signals[REQUEST_QUEUED], 0, msg);
1092 }
1093
1094 /**
1095  * SoupSessionCallback:
1096  * @session: the session
1097  * @msg: the message that has finished
1098  * @user_data: the data passed to soup_session_queue_message
1099  *
1100  * Prototype for the callback passed to soup_session_queue_message(),
1101  * qv.
1102  **/
1103
1104 /**
1105  * soup_session_queue_message:
1106  * @session: a #SoupSession
1107  * @msg: the message to queue
1108  * @callback: a #SoupSessionCallback which will be called after the
1109  * message completes or when an unrecoverable error occurs.
1110  * @user_data: a pointer passed to @callback.
1111  * 
1112  * Queues the message @msg for sending. All messages are processed
1113  * while the glib main loop runs. If @msg has been processed before,
1114  * any resources related to the time it was last sent are freed.
1115  *
1116  * Upon message completion, the callback specified in @callback will
1117  * be invoked (in the thread associated with @session's async
1118  * context). If after returning from this callback the message has not
1119  * been requeued, @msg will be unreffed.
1120  */
1121 void
1122 soup_session_queue_message (SoupSession *session, SoupMessage *msg,
1123                             SoupSessionCallback callback, gpointer user_data)
1124 {
1125         g_return_if_fail (SOUP_IS_SESSION (session));
1126         g_return_if_fail (SOUP_IS_MESSAGE (msg));
1127
1128         SOUP_SESSION_GET_CLASS (session)->queue_message (session, msg,
1129                                                          callback, user_data);
1130 }
1131
1132 static void
1133 requeue_message (SoupSession *session, SoupMessage *msg)
1134 {
1135         soup_message_set_io_status (msg, SOUP_MESSAGE_IO_STATUS_QUEUED);
1136 }
1137
1138 /**
1139  * soup_session_requeue_message:
1140  * @session: a #SoupSession
1141  * @msg: the message to requeue
1142  *
1143  * This causes @msg to be placed back on the queue to be attempted
1144  * again.
1145  **/
1146 void
1147 soup_session_requeue_message (SoupSession *session, SoupMessage *msg)
1148 {
1149         g_return_if_fail (SOUP_IS_SESSION (session));
1150         g_return_if_fail (SOUP_IS_MESSAGE (msg));
1151
1152         SOUP_SESSION_GET_CLASS (session)->requeue_message (session, msg);
1153 }
1154
1155
1156 /**
1157  * soup_session_send_message:
1158  * @session: a #SoupSession
1159  * @msg: the message to send
1160  * 
1161  * Synchronously send @msg. This call will not return until the
1162  * transfer is finished successfully or there is an unrecoverable
1163  * error.
1164  *
1165  * @msg is not freed upon return.
1166  *
1167  * Return value: the HTTP status code of the response
1168  */
1169 guint
1170 soup_session_send_message (SoupSession *session, SoupMessage *msg)
1171 {
1172         g_return_val_if_fail (SOUP_IS_SESSION (session), SOUP_STATUS_MALFORMED);
1173         g_return_val_if_fail (SOUP_IS_MESSAGE (msg), SOUP_STATUS_MALFORMED);
1174
1175         return SOUP_SESSION_GET_CLASS (session)->send_message (session, msg);
1176 }
1177
1178
1179 /**
1180  * soup_session_pause_message:
1181  * @session: a #SoupSession
1182  * @msg: a #SoupMessage currently running on @session
1183  *
1184  * Pauses HTTP I/O on @msg. Call soup_session_unpause_message() to
1185  * resume I/O.
1186  **/
1187 void
1188 soup_session_pause_message (SoupSession *session,
1189                             SoupMessage *msg)
1190 {
1191         g_return_if_fail (SOUP_IS_SESSION (session));
1192         g_return_if_fail (SOUP_IS_MESSAGE (msg));
1193
1194         soup_message_io_pause (msg);
1195 }
1196
1197 /**
1198  * soup_session_unpause_message:
1199  * @session: a #SoupSession
1200  * @msg: a #SoupMessage currently running on @session
1201  *
1202  * Resumes HTTP I/O on @msg. Use this to resume after calling
1203  * soup_sessino_pause_message().
1204  *
1205  * If @msg is being sent via blocking I/O, this will resume reading or
1206  * writing immediately. If @msg is using non-blocking I/O, then
1207  * reading or writing won't resume until you return to the main loop.
1208  **/
1209 void
1210 soup_session_unpause_message (SoupSession *session,
1211                               SoupMessage *msg)
1212 {
1213         g_return_if_fail (SOUP_IS_SESSION (session));
1214         g_return_if_fail (SOUP_IS_MESSAGE (msg));
1215
1216         soup_message_io_unpause (msg);
1217 }
1218
1219
1220 static void
1221 cancel_message (SoupSession *session, SoupMessage *msg, guint status_code)
1222 {
1223         SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1224
1225         soup_message_queue_remove_message (priv->queue, msg);
1226         soup_message_io_stop (msg);
1227         soup_message_set_status (msg, status_code);
1228         soup_message_finished (msg);
1229 }
1230
1231 /**
1232  * soup_session_cancel_message:
1233  * @session: a #SoupSession
1234  * @msg: the message to cancel
1235  * @status_code: status code to set on @msg (generally
1236  * %SOUP_STATUS_CANCELLED)
1237  *
1238  * Causes @session to immediately finish processing @msg, with a final
1239  * status_code of @status_code. Depending on when you cancel it, the
1240  * response state may be incomplete or inconsistent.
1241  **/
1242 void
1243 soup_session_cancel_message (SoupSession *session, SoupMessage *msg,
1244                              guint status_code)
1245 {
1246         g_return_if_fail (SOUP_IS_SESSION (session));
1247         g_return_if_fail (SOUP_IS_MESSAGE (msg));
1248
1249         SOUP_SESSION_GET_CLASS (session)->cancel_message (session, msg, status_code);
1250 }
1251
1252 static void
1253 gather_conns (gpointer key, gpointer host, gpointer data)
1254 {
1255         SoupConnection *conn = key;
1256         GSList **conns = data;
1257
1258         *conns = g_slist_prepend (*conns, conn);
1259 }
1260
1261 /**
1262  * soup_session_abort:
1263  * @session: the session
1264  *
1265  * Cancels all pending requests in @session.
1266  **/
1267 void
1268 soup_session_abort (SoupSession *session)
1269 {
1270         SoupSessionPrivate *priv;
1271         SoupMessageQueueIter iter;
1272         SoupMessage *msg;
1273         GSList *conns, *c;
1274
1275         g_return_if_fail (SOUP_IS_SESSION (session));
1276         priv = SOUP_SESSION_GET_PRIVATE (session);
1277
1278         for (msg = soup_message_queue_first (priv->queue, &iter);
1279              msg;
1280              msg = soup_message_queue_next (priv->queue, &iter)) {
1281                 soup_session_cancel_message (session, msg,
1282                                              SOUP_STATUS_CANCELLED);
1283         }
1284
1285         /* Close all connections */
1286         g_mutex_lock (priv->host_lock);
1287         conns = NULL;
1288         g_hash_table_foreach (priv->conns, gather_conns, &conns);
1289
1290         for (c = conns; c; c = c->next)
1291                 g_object_ref (c->data);
1292         g_mutex_unlock (priv->host_lock);
1293         for (c = conns; c; c = c->next) {
1294                 soup_connection_disconnect (c->data);
1295                 g_object_unref (c->data);
1296         }
1297
1298         g_slist_free (conns);
1299 }