soup-auth-manager: add soup_auth_manager_use_auth()
[platform/upstream/libsoup.git] / libsoup / soup-connection.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * soup-connection.c: A single HTTP/HTTPS connection
4  *
5  * Copyright (C) 2000-2003, Ximian, Inc.
6  */
7
8 #ifdef HAVE_CONFIG_H
9 #include <config.h>
10 #endif
11
12 #include "soup-connection.h"
13 #include "soup.h"
14 #include "soup-marshal.h"
15 #include "soup-message-queue.h"
16 #include "soup-misc-private.h"
17
18 typedef struct {
19         SoupSocket  *socket;
20
21         SoupURI *remote_uri, *proxy_uri;
22         SoupProxyURIResolver *proxy_resolver;
23         gboolean use_gproxyresolver;
24         GTlsDatabase *tlsdb;
25         gboolean ssl, ssl_strict, ssl_fallback;
26
27         GMainContext *async_context;
28         gboolean      use_thread_context;
29
30         SoupMessage *current_msg;
31         SoupConnectionState state;
32         time_t       unused_timeout;
33         guint        io_timeout, idle_timeout;
34         GSource     *idle_timeout_src;
35         gboolean     reusable;
36 } SoupConnectionPrivate;
37 #define SOUP_CONNECTION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_CONNECTION, SoupConnectionPrivate))
38
39 G_DEFINE_TYPE (SoupConnection, soup_connection, G_TYPE_OBJECT)
40
41 enum {
42         EVENT,
43         DISCONNECTED,
44         LAST_SIGNAL
45 };
46
47 static guint signals[LAST_SIGNAL] = { 0 };
48
49 enum {
50         PROP_0,
51
52         PROP_REMOTE_URI,
53         PROP_PROXY_RESOLVER,
54         PROP_SSL,
55         PROP_SSL_CREDS,
56         PROP_SSL_STRICT,
57         PROP_SSL_FALLBACK,
58         PROP_ASYNC_CONTEXT,
59         PROP_USE_THREAD_CONTEXT,
60         PROP_TIMEOUT,
61         PROP_IDLE_TIMEOUT,
62         PROP_STATE,
63
64         LAST_PROP
65 };
66
67 static void stop_idle_timer (SoupConnectionPrivate *priv);
68
69 /* Number of seconds after which we close a connection that hasn't yet
70  * been used.
71  */
72 #define SOUP_CONNECTION_UNUSED_TIMEOUT 3
73
74 static void
75 soup_connection_init (SoupConnection *conn)
76 {
77 }
78
79 static void
80 soup_connection_finalize (GObject *object)
81 {
82         SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (object);
83
84         g_clear_pointer (&priv->remote_uri, soup_uri_free);
85         g_clear_pointer (&priv->proxy_uri, soup_uri_free);
86         g_clear_object (&priv->tlsdb);
87         g_clear_object (&priv->proxy_resolver);
88         g_clear_pointer (&priv->async_context, g_main_context_unref);
89
90         G_OBJECT_CLASS (soup_connection_parent_class)->finalize (object);
91 }
92
93 static void
94 soup_connection_dispose (GObject *object)
95 {
96         SoupConnection *conn = SOUP_CONNECTION (object);
97         SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (conn);
98
99         stop_idle_timer (priv);
100
101         if (priv->socket) {
102                 g_warning ("Disposing connection while connected");
103                 soup_connection_disconnect (conn);
104         }
105
106         G_OBJECT_CLASS (soup_connection_parent_class)->dispose (object);
107 }
108
109 static void
110 soup_connection_set_property (GObject *object, guint prop_id,
111                               const GValue *value, GParamSpec *pspec)
112 {
113         SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (object);
114         SoupProxyURIResolver *proxy_resolver;
115
116         switch (prop_id) {
117         case PROP_REMOTE_URI:
118                 priv->remote_uri = g_value_dup_boxed (value);
119                 break;
120         case PROP_PROXY_RESOLVER:
121                 proxy_resolver = g_value_get_object (value);
122                 if (proxy_resolver && SOUP_IS_PROXY_RESOLVER_DEFAULT (proxy_resolver))
123                         priv->use_gproxyresolver = TRUE;
124                 else if (proxy_resolver)
125                         priv->proxy_resolver = g_object_ref (proxy_resolver);
126                 break;
127         case PROP_SSL:
128                 priv->ssl = g_value_get_boolean (value);
129                 break;
130         case PROP_SSL_CREDS:
131                 if (priv->tlsdb)
132                         g_object_unref (priv->tlsdb);
133                 priv->tlsdb = g_value_dup_object (value);
134                 break;
135         case PROP_SSL_STRICT:
136                 priv->ssl_strict = g_value_get_boolean (value);
137                 break;
138         case PROP_SSL_FALLBACK:
139                 priv->ssl_fallback = g_value_get_boolean (value);
140                 break;
141         case PROP_ASYNC_CONTEXT:
142                 priv->async_context = g_value_get_pointer (value);
143                 if (priv->async_context)
144                         g_main_context_ref (priv->async_context);
145                 break;
146         case PROP_USE_THREAD_CONTEXT:
147                 priv->use_thread_context = g_value_get_boolean (value);
148                 break;
149         case PROP_TIMEOUT:
150                 priv->io_timeout = g_value_get_uint (value);
151                 break;
152         case PROP_IDLE_TIMEOUT:
153                 priv->idle_timeout = g_value_get_uint (value);
154                 break;
155         case PROP_STATE:
156                 soup_connection_set_state (SOUP_CONNECTION (object), g_value_get_uint (value));
157                 break;
158         default:
159                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
160                 break;
161         }
162 }
163
164 static void
165 soup_connection_get_property (GObject *object, guint prop_id,
166                               GValue *value, GParamSpec *pspec)
167 {
168         SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (object);
169
170         switch (prop_id) {
171         case PROP_REMOTE_URI:
172                 g_value_set_boxed (value, priv->remote_uri);
173                 break;
174         case PROP_SSL:
175                 g_value_set_boolean (value, priv->ssl);
176                 break;
177         case PROP_SSL_CREDS:
178                 g_value_set_object (value, priv->tlsdb);
179                 break;
180         case PROP_SSL_STRICT:
181                 g_value_set_boolean (value, priv->ssl_strict);
182                 break;
183         case PROP_SSL_FALLBACK:
184                 g_value_set_boolean (value, priv->ssl_fallback);
185                 break;
186         case PROP_ASYNC_CONTEXT:
187                 g_value_set_pointer (value, priv->async_context ? g_main_context_ref (priv->async_context) : NULL);
188                 break;
189         case PROP_USE_THREAD_CONTEXT:
190                 g_value_set_boolean (value, priv->use_thread_context);
191                 break;
192         case PROP_TIMEOUT:
193                 g_value_set_uint (value, priv->io_timeout);
194                 break;
195         case PROP_IDLE_TIMEOUT:
196                 g_value_set_uint (value, priv->idle_timeout);
197                 break;
198         case PROP_STATE:
199                 g_value_set_enum (value, priv->state);
200                 break;
201         default:
202                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
203                 break;
204         }
205 }
206
207 static void
208 soup_connection_class_init (SoupConnectionClass *connection_class)
209 {
210         GObjectClass *object_class = G_OBJECT_CLASS (connection_class);
211
212         g_type_class_add_private (connection_class, sizeof (SoupConnectionPrivate));
213
214         /* virtual method override */
215         object_class->dispose = soup_connection_dispose;
216         object_class->finalize = soup_connection_finalize;
217         object_class->set_property = soup_connection_set_property;
218         object_class->get_property = soup_connection_get_property;
219
220         /* signals */
221         signals[EVENT] =
222                 g_signal_new ("event",
223                               G_OBJECT_CLASS_TYPE (object_class),
224                               G_SIGNAL_RUN_FIRST,
225                               0,
226                               NULL, NULL,
227                               NULL,
228                               G_TYPE_NONE, 2,
229                               G_TYPE_SOCKET_CLIENT_EVENT,
230                               G_TYPE_IO_STREAM);
231         signals[DISCONNECTED] =
232                 g_signal_new ("disconnected",
233                               G_OBJECT_CLASS_TYPE (object_class),
234                               G_SIGNAL_RUN_FIRST,
235                               G_STRUCT_OFFSET (SoupConnectionClass, disconnected),
236                               NULL, NULL,
237                               _soup_marshal_NONE__NONE,
238                               G_TYPE_NONE, 0);
239
240         /* properties */
241         g_object_class_install_property (
242                 object_class, PROP_REMOTE_URI,
243                 g_param_spec_boxed (SOUP_CONNECTION_REMOTE_URI,
244                                     "Remote URI",
245                                     "The URI of the HTTP server",
246                                     SOUP_TYPE_URI,
247                                     G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
248         g_object_class_install_property (
249                 object_class, PROP_PROXY_RESOLVER,
250                 g_param_spec_object (SOUP_CONNECTION_PROXY_RESOLVER,
251                                      "Proxy resolver",
252                                      "SoupProxyURIResolver to use",
253                                      SOUP_TYPE_PROXY_URI_RESOLVER,
254                                      G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
255         g_object_class_install_property (
256                 object_class, PROP_SSL,
257                 g_param_spec_boolean (SOUP_CONNECTION_SSL,
258                                       "SSL",
259                                       "Whether this is an SSL connection",
260                                       FALSE,
261                                       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
262         g_object_class_install_property (
263                 object_class, PROP_SSL_CREDS,
264                 g_param_spec_object (SOUP_CONNECTION_SSL_CREDENTIALS,
265                                      "SSL credentials",
266                                      "SSL credentials for this connection",
267                                      G_TYPE_TLS_DATABASE,
268                                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
269         g_object_class_install_property (
270                 object_class, PROP_SSL_STRICT,
271                 g_param_spec_boolean (SOUP_CONNECTION_SSL_STRICT,
272                                       "Strictly validate SSL certificates",
273                                       "Whether certificate errors should be considered a connection error",
274                                       TRUE,
275                                       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
276         g_object_class_install_property (
277                 object_class, PROP_SSL_FALLBACK,
278                 g_param_spec_boolean (SOUP_CONNECTION_SSL_FALLBACK,
279                                       "SSLv3 fallback",
280                                       "Use SSLv3 instead of TLS",
281                                       FALSE,
282                                       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
283         g_object_class_install_property (
284                 object_class, PROP_ASYNC_CONTEXT,
285                 g_param_spec_pointer (SOUP_CONNECTION_ASYNC_CONTEXT,
286                                       "Async GMainContext",
287                                       "GMainContext to dispatch this connection's async I/O in",
288                                       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
289         g_object_class_install_property (
290                 object_class, PROP_USE_THREAD_CONTEXT,
291                 g_param_spec_boolean (SOUP_CONNECTION_USE_THREAD_CONTEXT,
292                                       "Use thread context",
293                                       "Use g_main_context_get_thread_default",
294                                       FALSE,
295                                       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
296         g_object_class_install_property (
297                 object_class, PROP_TIMEOUT,
298                 g_param_spec_uint (SOUP_CONNECTION_TIMEOUT,
299                                    "Timeout value",
300                                    "Value in seconds to timeout a blocking I/O",
301                                    0, G_MAXUINT, 0,
302                                    G_PARAM_READWRITE));
303         g_object_class_install_property (
304                 object_class, PROP_IDLE_TIMEOUT,
305                 g_param_spec_uint (SOUP_CONNECTION_IDLE_TIMEOUT,
306                                    "Idle Timeout",
307                                    "Connection lifetime when idle",
308                                    0, G_MAXUINT, 0,
309                                    G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
310         g_object_class_install_property (
311                 object_class, PROP_STATE,
312                 g_param_spec_enum (SOUP_CONNECTION_STATE,
313                                    "Connection state",
314                                    "Current state of connection",
315                                    SOUP_TYPE_CONNECTION_STATE, SOUP_CONNECTION_NEW,
316                                    G_PARAM_READWRITE));
317 }
318
319 static void
320 soup_connection_event (SoupConnection      *conn,
321                        GSocketClientEvent   event,
322                        GIOStream           *connection)
323 {
324         SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (conn);
325
326         if (!connection && priv->socket)
327                 connection = soup_socket_get_connection (priv->socket);
328
329         g_signal_emit (conn, signals[EVENT], 0,
330                        event, connection);
331 }
332
333 static gboolean
334 idle_timeout (gpointer conn)
335 {
336         soup_connection_disconnect (conn);
337         return FALSE;
338 }
339
340 static void
341 start_idle_timer (SoupConnection *conn)
342 {
343         SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (conn);
344
345         if (priv->idle_timeout > 0 && !priv->idle_timeout_src) {
346                 priv->idle_timeout_src =
347                         soup_add_timeout (priv->async_context,
348                                           priv->idle_timeout * 1000,
349                                           idle_timeout, conn);
350         }
351 }
352
353 static void
354 stop_idle_timer (SoupConnectionPrivate *priv)
355 {
356         if (priv->idle_timeout_src) {
357                 g_source_destroy (priv->idle_timeout_src);
358                 priv->idle_timeout_src = NULL;
359         }
360 }
361
362 static void
363 current_msg_got_body (SoupMessage *msg, gpointer user_data)
364 {
365         SoupConnection *conn = user_data;
366         SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (conn);
367
368         priv->unused_timeout = 0;
369
370         if (priv->proxy_uri &&
371             msg->method == SOUP_METHOD_CONNECT &&
372             SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
373                 soup_connection_event (conn, G_SOCKET_CLIENT_PROXY_NEGOTIATED, NULL);
374
375                 /* We're now effectively no longer proxying */
376                 g_clear_pointer (&priv->proxy_uri, soup_uri_free);
377         }
378
379         priv->reusable = soup_message_is_keepalive (msg);
380 }
381
382 static void
383 clear_current_msg (SoupConnection *conn)
384 {
385         SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (conn);
386         SoupMessage *msg;
387
388         msg = priv->current_msg;
389         priv->current_msg = NULL;
390
391         g_signal_handlers_disconnect_by_func (msg, G_CALLBACK (current_msg_got_body), conn);
392         g_object_unref (msg);
393 }
394
395 static void
396 set_current_msg (SoupConnection *conn, SoupMessage *msg)
397 {
398         SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (conn);
399
400         g_return_if_fail (priv->state == SOUP_CONNECTION_IN_USE);
401
402         g_object_freeze_notify (G_OBJECT (conn));
403
404         if (priv->current_msg) {
405                 g_return_if_fail (priv->current_msg->method == SOUP_METHOD_CONNECT);
406                 clear_current_msg (conn);
407         }
408
409         stop_idle_timer (priv);
410
411         priv->current_msg = g_object_ref (msg);
412         priv->reusable = FALSE;
413
414         g_signal_connect (msg, "got-body",
415                           G_CALLBACK (current_msg_got_body), conn);
416
417         if (priv->proxy_uri && msg->method == SOUP_METHOD_CONNECT)
418                 soup_connection_event (conn, G_SOCKET_CLIENT_PROXY_NEGOTIATING, NULL);
419
420         g_object_thaw_notify (G_OBJECT (conn));
421 }
422
423 static void
424 proxy_socket_event (SoupSocket          *socket,
425                     GSocketClientEvent   event,
426                     GIOStream           *connection,
427                     gpointer             user_data)
428 {
429         SoupConnection *conn = user_data;
430
431         /* We handle COMPLETE ourselves */
432         if (event != G_SOCKET_CLIENT_COMPLETE)
433                 soup_connection_event (conn, event, connection);
434 }
435
436 typedef struct {
437         SoupConnection *conn;
438         SoupConnectionCallback callback;
439         gpointer callback_data;
440         GCancellable *cancellable;
441         guint event_id;
442 } SoupConnectionAsyncConnectData;
443
444 static void
445 socket_connect_finished (SoupSocket *socket, guint status, gpointer user_data)
446 {
447         SoupConnectionAsyncConnectData *data = user_data;
448         SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (data->conn);
449
450         g_signal_handler_disconnect (socket, data->event_id);
451
452         if (SOUP_STATUS_IS_SUCCESSFUL (status)) {
453                 if (priv->ssl && !priv->proxy_uri) {
454                         soup_connection_event (data->conn,
455                                                G_SOCKET_CLIENT_TLS_HANDSHAKED,
456                                                NULL);
457                 }
458                 if (!priv->ssl || !priv->proxy_uri) {
459                         soup_connection_event (data->conn,
460                                                G_SOCKET_CLIENT_COMPLETE,
461                                                NULL);
462                 }
463
464                 soup_connection_set_state (data->conn, SOUP_CONNECTION_IN_USE);
465                 priv->unused_timeout = time (NULL) + SOUP_CONNECTION_UNUSED_TIMEOUT;
466                 start_idle_timer (data->conn);
467         } else if (status == SOUP_STATUS_TLS_FAILED) {
468                 priv->ssl_fallback = TRUE;
469                 status = SOUP_STATUS_TRY_AGAIN;
470         }
471
472         if (data->callback) {
473                 if (priv->proxy_uri != NULL)
474                         status = soup_status_proxify (status);
475                 data->callback (data->conn, status, data->callback_data);
476         }
477         if (!SOUP_STATUS_IS_SUCCESSFUL (status) && status != SOUP_STATUS_TRY_AGAIN)
478                 soup_connection_disconnect (data->conn);
479         g_object_unref (data->conn);
480         if (data->cancellable)
481                 g_object_unref (data->cancellable);
482         g_slice_free (SoupConnectionAsyncConnectData, data);
483 }
484
485 static void
486 socket_connect_result (SoupSocket *sock, guint status, gpointer user_data)
487 {
488         SoupConnectionAsyncConnectData *data = user_data;
489         SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (data->conn);
490
491         if (!SOUP_STATUS_IS_SUCCESSFUL (status)) {
492                 socket_connect_finished (sock, status, data);
493                 return;
494         }
495
496         if (priv->use_gproxyresolver)
497                 priv->proxy_uri = soup_socket_get_http_proxy_uri (priv->socket);
498
499         if (priv->ssl && !priv->proxy_uri) {
500                 if (soup_socket_start_ssl (sock, data->cancellable)) {
501                         soup_connection_event (data->conn,
502                                                G_SOCKET_CLIENT_TLS_HANDSHAKING,
503                                                NULL);
504                         soup_socket_handshake_async (sock, data->cancellable,
505                                                      socket_connect_finished, data);
506                         return;
507                 }
508
509                 status = SOUP_STATUS_SSL_FAILED;
510         }
511
512         socket_connect_finished (sock, status, data);
513 }
514
515 static void
516 connect_async_to_uri (SoupConnectionAsyncConnectData *data, SoupURI *uri)
517 {
518         SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (data->conn);
519         SoupAddress *remote_addr;
520
521         remote_addr = soup_address_new (uri->host, uri->port);
522         priv->socket =
523                 soup_socket_new (SOUP_SOCKET_REMOTE_ADDRESS, remote_addr,
524                                  SOUP_SOCKET_SSL_CREDENTIALS, priv->tlsdb,
525                                  SOUP_SOCKET_SSL_STRICT, priv->ssl_strict,
526                                  SOUP_SOCKET_SSL_FALLBACK, priv->ssl_fallback,
527                                  SOUP_SOCKET_ASYNC_CONTEXT, priv->async_context,
528                                  SOUP_SOCKET_USE_THREAD_CONTEXT, priv->use_thread_context,
529                                  SOUP_SOCKET_USE_PROXY, priv->use_gproxyresolver,
530                                  SOUP_SOCKET_TIMEOUT, priv->io_timeout,
531                                  SOUP_SOCKET_CLEAN_DISPOSE, TRUE,
532                                  NULL);
533         g_object_unref (remote_addr);
534
535         data->event_id = g_signal_connect (priv->socket, "event",
536                                            G_CALLBACK (proxy_socket_event),
537                                            data->conn);
538
539         soup_socket_connect_async (priv->socket, data->cancellable,
540                                    socket_connect_result, data);
541 }
542
543 static void
544 proxy_resolver_result (SoupProxyURIResolver *resolver,
545                        guint status, SoupURI *proxy_uri,
546                        gpointer user_data)
547 {
548         SoupConnectionAsyncConnectData *data = user_data;
549         SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (data->conn);
550
551         if (status != SOUP_STATUS_OK) {
552                 socket_connect_finished (NULL, status, data);
553                 return;
554         }
555
556         if (proxy_uri) {
557                 priv->proxy_uri = soup_uri_copy (proxy_uri);
558                 connect_async_to_uri (data, proxy_uri);
559         } else 
560                 connect_async_to_uri (data, priv->remote_uri);
561 }
562
563 void
564 soup_connection_connect_async (SoupConnection *conn,
565                                GCancellable *cancellable,
566                                SoupConnectionCallback callback,
567                                gpointer user_data)
568 {
569         SoupConnectionAsyncConnectData *data;
570         SoupConnectionPrivate *priv;
571         GMainContext *async_context;
572
573         g_return_if_fail (SOUP_IS_CONNECTION (conn));
574         priv = SOUP_CONNECTION_GET_PRIVATE (conn);
575         g_return_if_fail (priv->socket == NULL);
576
577         soup_connection_set_state (conn, SOUP_CONNECTION_CONNECTING);
578
579         data = g_slice_new (SoupConnectionAsyncConnectData);
580         data->conn = g_object_ref (conn);
581         data->callback = callback;
582         data->callback_data = user_data;
583         data->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
584
585         if (!priv->proxy_resolver) {
586                 connect_async_to_uri (data, priv->remote_uri);
587                 return;
588         }
589
590         if (priv->use_thread_context)
591                 async_context = g_main_context_get_thread_default ();
592         else
593                 async_context = priv->async_context;
594
595         soup_proxy_uri_resolver_get_proxy_uri_async (priv->proxy_resolver,
596                                                      priv->remote_uri,
597                                                      async_context,
598                                                      cancellable,
599                                                      proxy_resolver_result,
600                                                      data);
601 }
602
603 guint
604 soup_connection_connect_sync (SoupConnection *conn, GCancellable *cancellable)
605 {
606         SoupConnectionPrivate *priv;
607         guint status, event_id = 0;
608         SoupURI *connect_uri;
609         SoupAddress *remote_addr;
610
611         g_return_val_if_fail (SOUP_IS_CONNECTION (conn), SOUP_STATUS_MALFORMED);
612         priv = SOUP_CONNECTION_GET_PRIVATE (conn);
613         g_return_val_if_fail (priv->socket == NULL, SOUP_STATUS_MALFORMED);
614
615         soup_connection_set_state (conn, SOUP_CONNECTION_CONNECTING);
616
617         if (priv->proxy_resolver) {
618                 status = soup_proxy_uri_resolver_get_proxy_uri_sync (priv->proxy_resolver,
619                                                                      priv->remote_uri,
620                                                                      cancellable,
621                                                                      &priv->proxy_uri);
622                 if (status != SOUP_STATUS_OK)
623                         goto fail;
624
625                 if (priv->proxy_uri)
626                         connect_uri = priv->proxy_uri;
627                 else
628                         connect_uri = priv->remote_uri;
629         } else
630                 connect_uri = priv->remote_uri;
631
632         remote_addr = soup_address_new (connect_uri->host, connect_uri->port);
633         priv->socket =
634                 soup_socket_new (SOUP_SOCKET_REMOTE_ADDRESS, remote_addr,
635                                  SOUP_SOCKET_USE_PROXY, priv->use_gproxyresolver,
636                                  SOUP_SOCKET_SSL_CREDENTIALS, priv->tlsdb,
637                                  SOUP_SOCKET_SSL_STRICT, priv->ssl_strict,
638                                  SOUP_SOCKET_SSL_FALLBACK, priv->ssl_fallback,
639                                  SOUP_SOCKET_FLAG_NONBLOCKING, FALSE,
640                                  SOUP_SOCKET_TIMEOUT, priv->io_timeout,
641                                  SOUP_SOCKET_CLEAN_DISPOSE, TRUE,
642                                  NULL);
643         g_object_unref (remote_addr);
644         event_id = g_signal_connect (priv->socket, "event",
645                                      G_CALLBACK (proxy_socket_event), conn);
646         status = soup_socket_connect_sync (priv->socket, cancellable);
647
648         if (!SOUP_STATUS_IS_SUCCESSFUL (status))
649                 goto fail;
650
651         if (priv->use_gproxyresolver)
652                 priv->proxy_uri = soup_socket_get_http_proxy_uri (priv->socket);
653
654         if (priv->ssl && !priv->proxy_uri) {
655                 if (!soup_socket_start_ssl (priv->socket, cancellable))
656                         status = SOUP_STATUS_SSL_FAILED;
657                 else {
658                         soup_connection_event (conn,
659                                                G_SOCKET_CLIENT_TLS_HANDSHAKING,
660                                                NULL);
661                         status = soup_socket_handshake_sync (priv->socket, cancellable);
662                         if (status == SOUP_STATUS_OK) {
663                                 soup_connection_event (conn,
664                                                        G_SOCKET_CLIENT_TLS_HANDSHAKED,
665                                                        NULL);
666                         } else if (status == SOUP_STATUS_TLS_FAILED) {
667                                 priv->ssl_fallback = TRUE;
668                                 status = SOUP_STATUS_TRY_AGAIN;
669                         }
670                 }
671         }
672
673         if (SOUP_STATUS_IS_SUCCESSFUL (status)) {
674                 if (!priv->ssl || !priv->proxy_uri) {
675                         soup_connection_event (conn,
676                                                G_SOCKET_CLIENT_COMPLETE,
677                                                NULL);
678                 }
679                 soup_connection_set_state (conn, SOUP_CONNECTION_IN_USE);
680                 priv->unused_timeout = time (NULL) + SOUP_CONNECTION_UNUSED_TIMEOUT;
681                 start_idle_timer (conn);
682         } else if (status != SOUP_STATUS_TRY_AGAIN) {
683         fail:
684                 if (priv->socket) {
685                         soup_socket_disconnect (priv->socket);
686                         g_object_unref (priv->socket);
687                         priv->socket = NULL;
688                 }
689
690                 soup_connection_disconnect (conn);
691         }
692
693         if (priv->socket && event_id)
694                 g_signal_handler_disconnect (priv->socket, event_id);
695
696         if (priv->proxy_uri != NULL)
697                 status = soup_status_proxify (status);
698         return status;
699 }
700
701 gboolean
702 soup_connection_is_tunnelled (SoupConnection *conn)
703 {
704         SoupConnectionPrivate *priv;
705
706         g_return_val_if_fail (SOUP_IS_CONNECTION (conn), FALSE);
707         priv = SOUP_CONNECTION_GET_PRIVATE (conn);
708
709         return priv->ssl && priv->proxy_uri != NULL;
710 }
711
712 guint
713 soup_connection_start_ssl_sync (SoupConnection *conn,
714                                 GCancellable   *cancellable)
715 {
716         SoupConnectionPrivate *priv;
717         guint status;
718
719         g_return_val_if_fail (SOUP_IS_CONNECTION (conn), FALSE);
720         priv = SOUP_CONNECTION_GET_PRIVATE (conn);
721
722         if (!soup_socket_start_proxy_ssl (priv->socket,
723                                           priv->remote_uri->host,
724                                           cancellable)) {
725                 soup_connection_disconnect (conn);
726                 return SOUP_STATUS_SSL_FAILED;
727         }
728
729         soup_connection_event (conn, G_SOCKET_CLIENT_TLS_HANDSHAKING, NULL);
730         status = soup_socket_handshake_sync (priv->socket, cancellable);
731         if (status == SOUP_STATUS_OK) {
732                 soup_connection_event (conn, G_SOCKET_CLIENT_TLS_HANDSHAKED, NULL);
733                 soup_connection_event (conn, G_SOCKET_CLIENT_COMPLETE, NULL);
734         } else if (status == SOUP_STATUS_TLS_FAILED) {
735                 priv->ssl_fallback = TRUE;
736                 status = SOUP_STATUS_TRY_AGAIN;
737         }
738
739         if (!SOUP_STATUS_IS_SUCCESSFUL (status) && status != SOUP_STATUS_TRY_AGAIN)
740                 soup_connection_disconnect (conn);
741
742         return status;
743 }
744
745 static void
746 start_ssl_completed (SoupSocket *socket, guint status, gpointer user_data)
747 {
748         SoupConnectionAsyncConnectData *data = user_data;
749         SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (data->conn);
750
751         if (status == SOUP_STATUS_OK) {
752                 soup_connection_event (data->conn, G_SOCKET_CLIENT_TLS_HANDSHAKED, NULL);
753                 soup_connection_event (data->conn, G_SOCKET_CLIENT_COMPLETE, NULL);
754         } else if (status == SOUP_STATUS_TLS_FAILED) {
755                 priv->ssl_fallback = TRUE;
756                 status = SOUP_STATUS_TRY_AGAIN;
757         }
758
759         data->callback (data->conn, status, data->callback_data);
760         if (!SOUP_STATUS_IS_SUCCESSFUL (status) && status != SOUP_STATUS_TRY_AGAIN)
761                 soup_connection_disconnect (data->conn);
762         g_object_unref (data->conn);
763         g_slice_free (SoupConnectionAsyncConnectData, data);
764 }
765
766 static gboolean
767 idle_start_ssl_completed (gpointer user_data)
768 {
769         SoupConnectionAsyncConnectData *data = user_data;
770
771         start_ssl_completed (NULL, SOUP_STATUS_SSL_FAILED, data);
772         return FALSE;
773 }
774
775 void
776 soup_connection_start_ssl_async (SoupConnection   *conn,
777                                  GCancellable     *cancellable,
778                                  SoupConnectionCallback callback,
779                                  gpointer          user_data)
780 {
781         SoupConnectionPrivate *priv;
782         SoupConnectionAsyncConnectData *data;
783         GMainContext *async_context;
784
785         g_return_if_fail (SOUP_IS_CONNECTION (conn));
786         priv = SOUP_CONNECTION_GET_PRIVATE (conn);
787
788         data = g_slice_new (SoupConnectionAsyncConnectData);
789         data->conn = g_object_ref (conn);
790         data->callback = callback;
791         data->callback_data = user_data;
792
793         if (priv->use_thread_context)
794                 async_context = g_main_context_get_thread_default ();
795         else
796                 async_context = priv->async_context;
797
798         if (!soup_socket_start_proxy_ssl (priv->socket,
799                                           priv->remote_uri->host,
800                                           cancellable)) {
801                 soup_add_completion (async_context,
802                                      idle_start_ssl_completed, data);
803                 return;
804         }
805
806         soup_connection_event (conn, G_SOCKET_CLIENT_TLS_HANDSHAKING, NULL);
807         soup_socket_handshake_async (priv->socket, cancellable,
808                                      start_ssl_completed, data);
809 }
810
811 /**
812  * soup_connection_disconnect:
813  * @conn: a connection
814  *
815  * Disconnects @conn's socket and emits a %disconnected signal.
816  * After calling this, @conn will be essentially useless.
817  **/
818 void
819 soup_connection_disconnect (SoupConnection *conn)
820 {
821         SoupConnectionPrivate *priv;
822         SoupConnectionState old_state;
823
824         g_return_if_fail (SOUP_IS_CONNECTION (conn));
825         priv = SOUP_CONNECTION_GET_PRIVATE (conn);
826
827         old_state = priv->state;
828         if (old_state != SOUP_CONNECTION_DISCONNECTED)
829                 soup_connection_set_state (conn, SOUP_CONNECTION_DISCONNECTED);
830
831         if (priv->socket) {
832                 SoupSocket *socket = priv->socket;
833
834                 priv->socket = NULL;
835                 soup_socket_disconnect (socket);
836                 g_object_unref (socket);
837         }
838
839         if (old_state != SOUP_CONNECTION_DISCONNECTED)
840                 g_signal_emit (conn, signals[DISCONNECTED], 0);
841 }
842
843 SoupSocket *
844 soup_connection_get_socket (SoupConnection *conn)
845 {
846         g_return_val_if_fail (SOUP_IS_CONNECTION (conn), NULL);
847
848         return SOUP_CONNECTION_GET_PRIVATE (conn)->socket;
849 }
850
851 SoupURI *
852 soup_connection_get_remote_uri (SoupConnection *conn)
853 {
854         g_return_val_if_fail (SOUP_IS_CONNECTION (conn), NULL);
855
856         return SOUP_CONNECTION_GET_PRIVATE (conn)->remote_uri;
857 }
858
859 SoupURI *
860 soup_connection_get_proxy_uri (SoupConnection *conn)
861 {
862         g_return_val_if_fail (SOUP_IS_CONNECTION (conn), NULL);
863
864         return SOUP_CONNECTION_GET_PRIVATE (conn)->proxy_uri;
865 }
866
867 gboolean
868 soup_connection_is_via_proxy (SoupConnection *conn)
869 {
870         g_return_val_if_fail (SOUP_IS_CONNECTION (conn), FALSE);
871
872         return SOUP_CONNECTION_GET_PRIVATE (conn)->proxy_uri != NULL;
873 }
874
875 SoupConnectionState
876 soup_connection_get_state (SoupConnection *conn)
877 {
878         SoupConnectionPrivate *priv;
879
880         g_return_val_if_fail (SOUP_IS_CONNECTION (conn),
881                               SOUP_CONNECTION_DISCONNECTED);
882         priv = SOUP_CONNECTION_GET_PRIVATE (conn);
883
884         if (priv->state == SOUP_CONNECTION_IDLE &&
885             g_socket_condition_check (soup_socket_get_gsocket (priv->socket), G_IO_IN))
886                 soup_connection_set_state (conn, SOUP_CONNECTION_REMOTE_DISCONNECTED);
887
888         if (priv->state == SOUP_CONNECTION_IDLE &&
889             priv->unused_timeout && priv->unused_timeout < time (NULL))
890                 soup_connection_set_state (conn, SOUP_CONNECTION_REMOTE_DISCONNECTED);
891
892         return priv->state;
893 }
894
895 void
896 soup_connection_set_state (SoupConnection *conn, SoupConnectionState state)
897 {
898         SoupConnectionPrivate *priv;
899
900         g_return_if_fail (SOUP_IS_CONNECTION (conn));
901         g_return_if_fail (state >= SOUP_CONNECTION_NEW &&
902                           state <= SOUP_CONNECTION_DISCONNECTED);
903
904         g_object_freeze_notify (G_OBJECT (conn));
905
906         priv = SOUP_CONNECTION_GET_PRIVATE (conn);
907
908         if (priv->current_msg) {
909                 g_warn_if_fail (state == SOUP_CONNECTION_IDLE ||
910                                 state == SOUP_CONNECTION_DISCONNECTED);
911                 clear_current_msg (conn);
912         }
913
914         if (state == SOUP_CONNECTION_IDLE && !priv->reusable) {
915                 /* This will recursively call set_state() */
916                 soup_connection_disconnect (conn);
917         } else {
918                 priv->state = state;
919
920                 if (priv->state == SOUP_CONNECTION_IDLE)
921                         start_idle_timer (conn);
922
923                 g_object_notify (G_OBJECT (conn), "state");
924         }
925
926         g_object_thaw_notify (G_OBJECT (conn));
927 }
928
929 gboolean
930 soup_connection_get_ever_used (SoupConnection *conn)
931 {
932         g_return_val_if_fail (SOUP_IS_CONNECTION (conn), FALSE);
933
934         return SOUP_CONNECTION_GET_PRIVATE (conn)->unused_timeout == 0;
935 }
936
937 gboolean
938 soup_connection_get_ssl_fallback (SoupConnection *conn)
939 {
940         return SOUP_CONNECTION_GET_PRIVATE (conn)->ssl_fallback;
941 }
942
943 void
944 soup_connection_send_request (SoupConnection          *conn,
945                               SoupMessageQueueItem    *item,
946                               SoupMessageCompletionFn  completion_cb,
947                               gpointer                 user_data)
948 {
949         SoupConnectionPrivate *priv;
950
951         g_return_if_fail (SOUP_IS_CONNECTION (conn));
952         g_return_if_fail (item != NULL);
953         priv = SOUP_CONNECTION_GET_PRIVATE (conn);
954         g_return_if_fail (priv->state != SOUP_CONNECTION_NEW &&
955                           priv->state != SOUP_CONNECTION_DISCONNECTED);
956
957         if (item->msg != priv->current_msg)
958                 set_current_msg (conn, item->msg);
959         soup_message_send_request (item, completion_cb, user_data);
960 }