Change the SoupURI properties to SoupAddress properties.
[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 <unistd.h>
13 #include <string.h>
14 #include <stdlib.h>
15 #include <glib.h>
16
17 #include <fcntl.h>
18 #include <sys/types.h>
19
20 #include "soup-address.h"
21 #include "soup-connection.h"
22 #include "soup-marshal.h"
23 #include "soup-message.h"
24 #include "soup-message-private.h"
25 #include "soup-misc.h"
26 #include "soup-socket.h"
27 #include "soup-ssl.h"
28 #include "soup-uri.h"
29
30 typedef enum {
31         SOUP_CONNECTION_MODE_DIRECT,
32         SOUP_CONNECTION_MODE_PROXY,
33         SOUP_CONNECTION_MODE_TUNNEL
34 } SoupConnectionMode;
35
36 typedef struct {
37         SoupSocket  *socket;
38
39         /* proxy_addr is the address of the proxy server we are
40          * connected to, if any. server_addr is the address of the
41          * origin server. conn_addr is the uri of the host we are
42          * actually directly connected to, which will be proxy_addr if
43          * there's a proxy and server_addr if not.
44          */
45         SoupAddress *proxy_addr, *server_addr, *conn_addr;
46         gpointer     ssl_creds;
47
48         SoupConnectionMode  mode;
49
50         GMainContext      *async_context;
51
52         SoupMessage *cur_req;
53         time_t       last_used;
54         gboolean     connected, in_use;
55         guint        io_timeout, idle_timeout;
56         GSource     *idle_timeout_src;
57 } SoupConnectionPrivate;
58 #define SOUP_CONNECTION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_CONNECTION, SoupConnectionPrivate))
59
60 G_DEFINE_TYPE (SoupConnection, soup_connection, G_TYPE_OBJECT)
61
62 enum {
63         CONNECT_RESULT,
64         DISCONNECTED,
65         REQUEST_STARTED,
66         LAST_SIGNAL
67 };
68
69 static guint signals[LAST_SIGNAL] = { 0 };
70
71 enum {
72         PROP_0,
73
74         PROP_SERVER_ADDRESS,
75         PROP_PROXY_ADDRESS,
76         PROP_SSL_CREDS,
77         PROP_ASYNC_CONTEXT,
78         PROP_TIMEOUT,
79         PROP_IDLE_TIMEOUT,
80
81         LAST_PROP
82 };
83
84 static void set_property (GObject *object, guint prop_id,
85                           const GValue *value, GParamSpec *pspec);
86 static void get_property (GObject *object, guint prop_id,
87                           GValue *value, GParamSpec *pspec);
88
89 static void stop_idle_timer (SoupConnectionPrivate *priv);
90 static void send_request (SoupConnection *conn, SoupMessage *req);
91 static void clear_current_request (SoupConnection *conn);
92
93 static void
94 soup_connection_init (SoupConnection *conn)
95 {
96         ;
97 }
98
99 static void
100 finalize (GObject *object)
101 {
102         SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (object);
103
104         if (priv->proxy_addr)
105                 g_object_unref (priv->proxy_addr);
106         if (priv->server_addr)
107                 g_object_unref (priv->server_addr);
108
109         if (priv->async_context)
110                 g_main_context_unref (priv->async_context);
111
112         G_OBJECT_CLASS (soup_connection_parent_class)->finalize (object);
113 }
114
115 static void
116 dispose (GObject *object)
117 {
118         SoupConnection *conn = SOUP_CONNECTION (object);
119         SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (conn);
120
121         stop_idle_timer (priv);
122         /* Make sure clear_current_request doesn't re-establish the timeout */
123         priv->idle_timeout = 0;
124
125         clear_current_request (conn);
126         soup_connection_disconnect (conn);
127
128         G_OBJECT_CLASS (soup_connection_parent_class)->dispose (object);
129 }
130
131 static void
132 soup_connection_class_init (SoupConnectionClass *connection_class)
133 {
134         GObjectClass *object_class = G_OBJECT_CLASS (connection_class);
135
136         g_type_class_add_private (connection_class, sizeof (SoupConnectionPrivate));
137
138         /* virtual method definition */
139         connection_class->send_request = send_request;
140
141         /* virtual method override */
142         object_class->dispose = dispose;
143         object_class->finalize = finalize;
144         object_class->set_property = set_property;
145         object_class->get_property = get_property;
146
147         /* signals */
148
149         signals[CONNECT_RESULT] =
150                 g_signal_new ("connect_result",
151                               G_OBJECT_CLASS_TYPE (object_class),
152                               G_SIGNAL_RUN_FIRST,
153                               G_STRUCT_OFFSET (SoupConnectionClass, connect_result),
154                               NULL, NULL,
155                               soup_marshal_NONE__INT,
156                               G_TYPE_NONE, 1,
157                               G_TYPE_INT);
158         signals[DISCONNECTED] =
159                 g_signal_new ("disconnected",
160                               G_OBJECT_CLASS_TYPE (object_class),
161                               G_SIGNAL_RUN_FIRST,
162                               G_STRUCT_OFFSET (SoupConnectionClass, disconnected),
163                               NULL, NULL,
164                               soup_marshal_NONE__NONE,
165                               G_TYPE_NONE, 0);
166         signals[REQUEST_STARTED] =
167                 g_signal_new ("request-started",
168                               G_OBJECT_CLASS_TYPE (object_class),
169                               G_SIGNAL_RUN_FIRST,
170                               G_STRUCT_OFFSET (SoupConnectionClass, request_started),
171                               NULL, NULL,
172                               soup_marshal_NONE__OBJECT,
173                               G_TYPE_NONE, 1,
174                               SOUP_TYPE_MESSAGE);
175
176         /* properties */
177         g_object_class_install_property (
178                 object_class, PROP_SERVER_ADDRESS,
179                 g_param_spec_object (SOUP_CONNECTION_SERVER_ADDRESS,
180                                      "Server address",
181                                      "The address of the HTTP origin server for this connection",
182                                      SOUP_TYPE_ADDRESS,
183                                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
184         g_object_class_install_property (
185                 object_class, PROP_PROXY_ADDRESS,
186                 g_param_spec_object (SOUP_CONNECTION_PROXY_ADDRESS,
187                                      "Proxy address",
188                                      "The address of the HTTP Proxy to use for this connection",
189                                      SOUP_TYPE_ADDRESS,
190                                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
191         g_object_class_install_property (
192                 object_class, PROP_SSL_CREDS,
193                 g_param_spec_pointer (SOUP_CONNECTION_SSL_CREDENTIALS,
194                                       "SSL credentials",
195                                       "Opaque SSL credentials for this connection",
196                                       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
197         g_object_class_install_property (
198                 object_class, PROP_ASYNC_CONTEXT,
199                 g_param_spec_pointer (SOUP_CONNECTION_ASYNC_CONTEXT,
200                                       "Async GMainContext",
201                                       "GMainContext to dispatch this connection's async I/O in",
202                                       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
203         g_object_class_install_property (
204                 object_class, PROP_TIMEOUT,
205                 g_param_spec_uint (SOUP_CONNECTION_TIMEOUT,
206                                    "Timeout value",
207                                    "Value in seconds to timeout a blocking I/O",
208                                    0, G_MAXUINT, 0,
209                                    G_PARAM_READWRITE));
210         g_object_class_install_property (
211                 object_class, PROP_IDLE_TIMEOUT,
212                 g_param_spec_uint (SOUP_CONNECTION_IDLE_TIMEOUT,
213                                    "Idle Timeout",
214                                    "Connection lifetime when idle",
215                                    0, G_MAXUINT, 0,
216                                    G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
217 }
218
219
220 /**
221  * soup_connection_new:
222  * @propname1: name of first property to set
223  * @...: value of @propname1, followed by additional property/value pairs
224  *
225  * Creates an HTTP connection. There are three possibilities:
226  *
227  * If you set %SOUP_CONNECTION_SERVER_ADDRESS and not
228  * %SOUP_CONNECTION_PROXY_ADDRESS, this will be a direct connection to
229  * the indicated origin server.
230  *
231  * If you set %SOUP_CONNECTION_PROXY_ADDRESS and not
232  * %SOUP_CONNECTION_SSL_CREDENTIALS, this will be a standard proxy
233  * connection, which can be used for requests to multiple origin
234  * servers.
235  *
236  * If you set %SOUP_CONNECTION_SERVER_ADDRESS,
237  * %SOUP_CONNECTION_PROXY_ADDRESS, and
238  * %SOUP_CONNECTION_SSL_CREDENTIALS, this will be a tunnel through the
239  * proxy to the origin server.
240  *
241  * You must call soup_connection_connect_async() or
242  * soup_connection_connect_sync() to connect it after creating it.
243  *
244  * Return value: the new connection (not yet ready for use).
245  **/
246 SoupConnection *
247 soup_connection_new (const char *propname1, ...)
248 {
249         SoupConnection *conn;
250         va_list ap;
251
252         va_start (ap, propname1);
253         conn = (SoupConnection *)g_object_new_valist (SOUP_TYPE_CONNECTION,
254                                                       propname1, ap);
255         va_end (ap);
256
257         return conn;
258 }
259
260 static void
261 set_property (GObject *object, guint prop_id,
262               const GValue *value, GParamSpec *pspec)
263 {
264         SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (object);
265
266         switch (prop_id) {
267         case PROP_SERVER_ADDRESS:
268                 priv->server_addr = g_value_dup_object (value);
269                 goto changed_connection;
270
271         case PROP_PROXY_ADDRESS:
272                 priv->proxy_addr = g_value_dup_object (value);
273                 goto changed_connection;
274
275         case PROP_SSL_CREDS:
276                 priv->ssl_creds = g_value_get_pointer (value);
277                 goto changed_connection;
278
279         changed_connection:
280                 if (priv->proxy_addr) {
281                         priv->conn_addr = priv->proxy_addr;
282                         if (priv->server_addr && priv->ssl_creds)
283                                 priv->mode = SOUP_CONNECTION_MODE_TUNNEL;
284                         else
285                                 priv->mode = SOUP_CONNECTION_MODE_PROXY;
286                 } else {
287                         priv->conn_addr = priv->server_addr;
288                         priv->mode = SOUP_CONNECTION_MODE_DIRECT;
289                 }
290                 break;
291
292         case PROP_ASYNC_CONTEXT:
293                 priv->async_context = g_value_get_pointer (value);
294                 if (priv->async_context)
295                         g_main_context_ref (priv->async_context);
296                 break;
297         case PROP_TIMEOUT:
298                 priv->io_timeout = g_value_get_uint (value);
299                 break;
300         case PROP_IDLE_TIMEOUT:
301                 priv->idle_timeout = g_value_get_uint (value);
302                 break;
303         default:
304                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
305                 break;
306         }
307 }
308
309 static void
310 get_property (GObject *object, guint prop_id,
311               GValue *value, GParamSpec *pspec)
312 {
313         SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (object);
314
315         switch (prop_id) {
316         case PROP_SERVER_ADDRESS:
317                 g_value_set_object (value, priv->server_addr);
318                 break;
319         case PROP_PROXY_ADDRESS:
320                 g_value_set_object (value, priv->proxy_addr);
321                 break;
322         case PROP_SSL_CREDS:
323                 g_value_set_pointer (value, priv->ssl_creds);
324                 break;
325         case PROP_ASYNC_CONTEXT:
326                 g_value_set_pointer (value, priv->async_context ? g_main_context_ref (priv->async_context) : NULL);
327                 break;
328         case PROP_TIMEOUT:
329                 g_value_set_uint (value, priv->io_timeout);
330                 break;
331         case PROP_IDLE_TIMEOUT:
332                 g_value_set_uint (value, priv->idle_timeout);
333                 break;
334         default:
335                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
336                 break;
337         }
338 }
339
340 static gboolean
341 idle_timeout (gpointer conn)
342 {
343         soup_connection_disconnect (conn);
344         return FALSE;
345 }
346
347 static void
348 start_idle_timer (SoupConnection *conn)
349 {
350         SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (conn);
351
352         if (priv->idle_timeout > 0 && !priv->idle_timeout_src) {
353                 priv->idle_timeout_src =
354                         soup_add_timeout (priv->async_context,
355                                           priv->idle_timeout * 1000,
356                                           idle_timeout, conn);
357         }
358 }
359
360 static void
361 stop_idle_timer (SoupConnectionPrivate *priv)
362 {
363         if (priv->idle_timeout_src) {
364                 g_source_destroy (priv->idle_timeout_src);
365                 priv->idle_timeout_src = NULL;
366         }
367 }
368
369 static void
370 set_current_request (SoupConnectionPrivate *priv, SoupMessage *req)
371 {
372         g_return_if_fail (priv->cur_req == NULL);
373
374         stop_idle_timer (priv);
375
376         soup_message_set_io_status (req, SOUP_MESSAGE_IO_STATUS_RUNNING);
377         priv->cur_req = req;
378         priv->in_use = TRUE;
379         g_object_add_weak_pointer (G_OBJECT (req), (gpointer)&priv->cur_req);
380 }
381
382 static void
383 clear_current_request (SoupConnection *conn)
384 {
385         SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (conn);
386
387         priv->in_use = FALSE;
388         start_idle_timer (conn);
389         if (priv->cur_req) {
390                 SoupMessage *cur_req = priv->cur_req;
391
392                 g_object_remove_weak_pointer (G_OBJECT (priv->cur_req),
393                                               (gpointer)&priv->cur_req);
394                 priv->cur_req = NULL;
395
396                 if (!soup_message_is_keepalive (cur_req))
397                         soup_connection_disconnect (conn);
398                 else {
399                         priv->last_used = time (NULL);
400                         soup_message_io_stop (cur_req);
401                 }
402         }
403 }
404
405 static void
406 socket_disconnected (SoupSocket *sock, gpointer conn)
407 {
408         soup_connection_disconnect (conn);
409 }
410
411 static inline guint
412 proxified_status (SoupConnectionPrivate *priv, guint status)
413 {
414         if (!priv->proxy_addr)
415                 return status;
416
417         if (status == SOUP_STATUS_CANT_RESOLVE)
418                 return SOUP_STATUS_CANT_RESOLVE_PROXY;
419         else if (status == SOUP_STATUS_CANT_CONNECT)
420                 return SOUP_STATUS_CANT_CONNECT_PROXY;
421         else
422                 return status;
423 }
424
425 static SoupMessage *
426 connect_message (SoupConnectionPrivate *priv)
427 {
428         SoupURI *uri;
429         SoupMessage *msg;
430
431         uri = soup_uri_new (NULL);
432         soup_uri_set_scheme (uri, SOUP_URI_SCHEME_HTTPS);
433         soup_uri_set_host (uri, soup_address_get_name (priv->server_addr));
434         soup_uri_set_port (uri, soup_address_get_port (priv->server_addr));
435         msg = soup_message_new_from_uri (SOUP_METHOD_CONNECT, uri);
436         soup_uri_free (uri);
437
438         return msg;
439 }
440
441 static void
442 tunnel_connect_finished (SoupMessage *msg, gpointer user_data)
443 {
444         SoupConnection *conn = user_data;
445         SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (conn);
446         guint status = msg->status_code;
447
448         clear_current_request (conn);
449
450         if (SOUP_STATUS_IS_SUCCESSFUL (status) && priv->ssl_creds) {
451                 const char *server_name =
452                         soup_address_get_name (priv->server_addr);
453                 if (soup_socket_start_proxy_ssl (priv->socket, server_name,
454                                                  NULL))
455                         priv->connected = TRUE;
456                 else
457                         status = SOUP_STATUS_SSL_FAILED;
458         } else if (SOUP_STATUS_IS_REDIRECTION (status)) {
459                 /* Oops, the proxy thinks we're a web browser. */
460                 status = SOUP_STATUS_PROXY_AUTHENTICATION_REQUIRED;
461         }
462
463         g_signal_emit (conn, signals[CONNECT_RESULT], 0,
464                        proxified_status (priv, status));
465         g_object_unref (msg);
466 }
467
468 static void
469 tunnel_connect_restarted (SoupMessage *msg, gpointer user_data)
470 {
471         SoupConnection *conn = user_data;
472         guint status = msg->status_code;
473
474         /* We only allow one restart: if another one happens, treat
475          * it as "finished".
476          */
477         g_signal_handlers_disconnect_by_func (msg, tunnel_connect_restarted, conn);
478         g_signal_connect (msg, "restarted",
479                           G_CALLBACK (tunnel_connect_finished), conn);
480
481         if (status == SOUP_STATUS_PROXY_AUTHENTICATION_REQUIRED) {
482                 /* Our parent session has handled the authentication
483                  * and attempted to restart the message.
484                  */
485                 if (soup_message_is_keepalive (msg)) {
486                         /* Connection is still open, so just send the
487                          * message again.
488                          */
489                         soup_connection_send_request (conn, msg);
490                 } else {
491                         /* Tell the session to try again. */
492                         soup_message_set_status (msg, SOUP_STATUS_TRY_AGAIN);
493                         soup_message_finished (msg);
494                 }
495         } else
496                 soup_message_finished (msg);
497 }
498
499 static void
500 socket_connect_result (SoupSocket *sock, guint status, gpointer user_data)
501 {
502         SoupConnection *conn = user_data;
503         SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (conn);
504
505         if (!SOUP_STATUS_IS_SUCCESSFUL (status))
506                 goto done;
507
508         if (priv->mode == SOUP_CONNECTION_MODE_DIRECT && priv->ssl_creds) {
509                 if (!soup_socket_start_ssl (sock, NULL)) {
510                         status = SOUP_STATUS_SSL_FAILED;
511                         goto done;
512                 }
513         }
514
515         if (priv->mode == SOUP_CONNECTION_MODE_TUNNEL) {
516                 SoupMessage *connect_msg = connect_message (priv);
517
518                 g_signal_connect (connect_msg, "restarted",
519                                   G_CALLBACK (tunnel_connect_restarted), conn);
520                 g_signal_connect (connect_msg, "finished",
521                                   G_CALLBACK (tunnel_connect_finished), conn);
522
523                 soup_connection_send_request (conn, connect_msg);
524                 return;
525         }
526
527         priv->connected = TRUE;
528         start_idle_timer (conn);
529
530  done:
531         g_signal_emit (conn, signals[CONNECT_RESULT], 0,
532                        proxified_status (priv, status));
533 }
534
535 /* from soup-misc.c... will eventually go away */
536 guint soup_signal_connect_once  (gpointer instance, const char *detailed_signal,
537                                  GCallback c_handler, gpointer data);
538
539 static void
540 address_resolved (SoupAddress *addr, guint status, gpointer data)
541 {
542         SoupConnection *conn = data;
543         SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (conn);
544
545         if (status != SOUP_STATUS_OK) {
546                 socket_connect_result (NULL, status, conn);
547                 return;
548         }
549
550         priv->socket =
551                 soup_socket_new (SOUP_SOCKET_REMOTE_ADDRESS, addr,
552                                  SOUP_SOCKET_SSL_CREDENTIALS, priv->ssl_creds,
553                                  SOUP_SOCKET_ASYNC_CONTEXT, priv->async_context,
554                                  NULL);
555         soup_socket_connect_async (priv->socket, NULL,
556                                    socket_connect_result, conn);
557         g_signal_connect (priv->socket, "disconnected",
558                           G_CALLBACK (socket_disconnected), conn);
559 }
560
561 /**
562  * soup_connection_connect_async:
563  * @conn: the connection
564  * @callback: callback to call when the connection succeeds or fails
565  * @user_data: data for @callback
566  *
567  * Asynchronously connects @conn.
568  **/
569 void
570 soup_connection_connect_async (SoupConnection *conn,
571                                SoupConnectionCallback callback,
572                                gpointer user_data)
573 {
574         SoupConnectionPrivate *priv;
575
576         g_return_if_fail (SOUP_IS_CONNECTION (conn));
577         priv = SOUP_CONNECTION_GET_PRIVATE (conn);
578         g_return_if_fail (priv->socket == NULL);
579
580         if (callback) {
581                 soup_signal_connect_once (conn, "connect_result",
582                                           G_CALLBACK (callback), user_data);
583         }
584
585         soup_address_resolve_async (priv->conn_addr, priv->async_context, NULL,
586                                     address_resolved, conn);
587 }
588
589 /**
590  * soup_connection_connect_sync:
591  * @conn: the connection
592  *
593  * Synchronously connects @conn.
594  *
595  * Return value: the soup status
596  **/
597 guint
598 soup_connection_connect_sync (SoupConnection *conn)
599 {
600         SoupConnectionPrivate *priv;
601         guint status;
602
603         g_return_val_if_fail (SOUP_IS_CONNECTION (conn), SOUP_STATUS_MALFORMED);
604         priv = SOUP_CONNECTION_GET_PRIVATE (conn);
605         g_return_val_if_fail (priv->socket == NULL, SOUP_STATUS_MALFORMED);
606
607         status = soup_address_resolve_sync (priv->conn_addr, NULL);
608         if (!SOUP_STATUS_IS_SUCCESSFUL (status))
609                 goto fail;
610
611         priv->socket =
612                 soup_socket_new (SOUP_SOCKET_REMOTE_ADDRESS, priv->conn_addr,
613                                  SOUP_SOCKET_SSL_CREDENTIALS, priv->ssl_creds,
614                                  SOUP_SOCKET_FLAG_NONBLOCKING, FALSE,
615                                  SOUP_SOCKET_TIMEOUT, priv->io_timeout,
616                                  NULL);
617
618         status = soup_socket_connect_sync (priv->socket, NULL);
619
620         if (!SOUP_STATUS_IS_SUCCESSFUL (status))
621                 goto fail;
622                 
623         g_signal_connect (priv->socket, "disconnected",
624                           G_CALLBACK (socket_disconnected), conn);
625
626         if (priv->mode == SOUP_CONNECTION_MODE_DIRECT && priv->ssl_creds) {
627                 if (!soup_socket_start_ssl (priv->socket, NULL)) {
628                         status = SOUP_STATUS_SSL_FAILED;
629                         goto fail;
630                 }
631         }
632
633         if (priv->mode == SOUP_CONNECTION_MODE_TUNNEL) {
634                 SoupMessage *connect_msg = connect_message (priv);
635
636                 soup_connection_send_request (conn, connect_msg);
637                 status = connect_msg->status_code;
638
639                 if (status == SOUP_STATUS_PROXY_AUTHENTICATION_REQUIRED &&
640                     SOUP_MESSAGE_IS_STARTING (connect_msg)) {
641                         if (soup_message_is_keepalive (connect_msg)) {
642                                 /* Try once more */
643                                 soup_connection_send_request (conn, connect_msg);
644                                 status = connect_msg->status_code;
645                         } else
646                                 status = SOUP_STATUS_TRY_AGAIN;
647                 }
648
649                 g_object_unref (connect_msg);
650
651                 if (SOUP_STATUS_IS_SUCCESSFUL (status)) {
652                         const char *server_name =
653                                 soup_address_get_name (priv->server_addr);
654                         if (!soup_socket_start_proxy_ssl (priv->socket,
655                                                           server_name,
656                                                           NULL))
657                                 status = SOUP_STATUS_SSL_FAILED;
658                 }
659         }
660
661         if (SOUP_STATUS_IS_SUCCESSFUL (status)) {
662                 priv->connected = TRUE;
663                 start_idle_timer (conn);
664         } else {
665         fail:
666                 if (priv->socket) {
667                         g_object_unref (priv->socket);
668                         priv->socket = NULL;
669                 }
670         }
671
672         status = proxified_status (priv, status);
673         g_signal_emit (conn, signals[CONNECT_RESULT], 0, status);
674         return status;
675 }
676
677
678 /**
679  * soup_connection_disconnect:
680  * @conn: a connection
681  *
682  * Disconnects @conn's socket and emits a %disconnected signal.
683  * After calling this, @conn will be essentially useless.
684  **/
685 void
686 soup_connection_disconnect (SoupConnection *conn)
687 {
688         SoupConnectionPrivate *priv;
689
690         g_return_if_fail (SOUP_IS_CONNECTION (conn));
691         priv = SOUP_CONNECTION_GET_PRIVATE (conn);
692
693         if (!priv->socket)
694                 return;
695
696         g_signal_handlers_disconnect_by_func (priv->socket,
697                                               socket_disconnected, conn);
698         soup_socket_disconnect (priv->socket);
699         g_object_unref (priv->socket);
700         priv->socket = NULL;
701
702         /* Don't emit "disconnected" if we aren't yet connected */
703         if (!priv->connected)
704                 return;
705
706         priv->connected = FALSE;
707
708         if (priv->cur_req &&
709             priv->cur_req->status_code == SOUP_STATUS_IO_ERROR &&
710             priv->last_used != 0) {
711                 /* There was a message queued on this connection, but
712                  * the socket was closed while it was being sent.
713                  * Since last_used is not 0, then that means at least
714                  * one message was successfully sent on this
715                  * connection before, and so the most likely cause of
716                  * the IO_ERROR is that the connection was idle for
717                  * too long and the server timed out and closed it
718                  * (and we didn't notice until after we started
719                  * sending the message). So we want the message to get
720                  * tried again on a new connection. The only code path
721                  * that could have gotten us to this point is through
722                  * the call to io_cleanup() in
723                  * soup_message_io_finished(), and so all we need to
724                  * do to get the message requeued in this case is to
725                  * change its status.
726                  */
727                 soup_message_cleanup_response (priv->cur_req);
728                 soup_message_set_io_status (priv->cur_req,
729                                             SOUP_MESSAGE_IO_STATUS_QUEUED);
730         }
731
732         /* If cur_req is non-NULL but priv->last_used is 0, then that
733          * means this was the first message to be sent on this
734          * connection, and it failed, so the error probably means that
735          * there's some network or server problem, so we let the
736          * IO_ERROR be returned to the caller.
737          *
738          * (Of course, it's also possible that the error in the
739          * last_used != 0 case was because of a network/server problem
740          * too. It's even possible that the message crashed the
741          * server. In this case, requeuing it was the wrong thing to
742          * do, but presumably, the next attempt will also get an
743          * error, and eventually the message will be requeued onto a
744          * fresh connection and get an error, at which point the error
745          * will finally be returned to the caller.)
746          */
747
748         /* NB: this might cause conn to be destroyed. */
749         g_signal_emit (conn, signals[DISCONNECTED], 0);
750 }
751
752 SoupSocket *
753 soup_connection_get_socket (SoupConnection *conn)
754 {
755         g_return_val_if_fail (SOUP_IS_CONNECTION (conn), NULL);
756
757         return SOUP_CONNECTION_GET_PRIVATE (conn)->socket;
758 }
759
760 /**
761  * soup_connection_is_in_use:
762  * @conn: a connection
763  *
764  * Tests whether or not @conn is in use.
765  *
766  * Return value: %TRUE if there is currently a request being processed
767  * on @conn.
768  **/
769 gboolean
770 soup_connection_is_in_use (SoupConnection *conn)
771 {
772         g_return_val_if_fail (SOUP_IS_CONNECTION (conn), FALSE);
773
774         return SOUP_CONNECTION_GET_PRIVATE (conn)->in_use;
775 }
776
777 /**
778  * soup_connection_last_used:
779  * @conn: a #SoupConnection.
780  *
781  * Returns the last time a response was received on @conn.
782  *
783  * Return value: the last time a response was received on @conn, or 0
784  * if @conn has not been used yet.
785  */
786 time_t
787 soup_connection_last_used (SoupConnection *conn)
788 {
789         g_return_val_if_fail (SOUP_IS_CONNECTION (conn), FALSE);
790
791         return SOUP_CONNECTION_GET_PRIVATE (conn)->last_used;
792 }
793
794 static void
795 send_request (SoupConnection *conn, SoupMessage *req)
796 {
797         SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (conn);
798
799         if (req != priv->cur_req) {
800                 set_current_request (priv, req);
801                 g_signal_emit (conn, signals[REQUEST_STARTED], 0, req);
802         }
803
804         soup_message_send_request (req, priv->socket, conn,
805                                    priv->mode == SOUP_CONNECTION_MODE_PROXY);
806 }
807
808 /**
809  * soup_connection_send_request:
810  * @conn: a #SoupConnection
811  * @req: a #SoupMessage
812  *
813  * Sends @req on @conn. This is a low-level function, intended for use
814  * by #SoupSession.
815  **/
816 void
817 soup_connection_send_request (SoupConnection *conn, SoupMessage *req)
818 {
819         g_return_if_fail (SOUP_IS_CONNECTION (conn));
820         g_return_if_fail (SOUP_IS_MESSAGE (req));
821         g_return_if_fail (SOUP_CONNECTION_GET_PRIVATE (conn)->socket != NULL);
822
823         SOUP_CONNECTION_GET_CLASS (conn)->send_request (conn, req);
824 }
825
826 /**
827  * soup_connection_reserve:
828  * @conn: a #SoupConnection
829  *
830  * Marks @conn as "in use" despite not actually having a message on
831  * it. This is used by #SoupSession to keep it from accidentally
832  * trying to queue two messages on the same connection from different
833  * threads at the same time.
834  **/
835 void
836 soup_connection_reserve (SoupConnection *conn)
837 {
838         g_return_if_fail (SOUP_IS_CONNECTION (conn));
839
840         SOUP_CONNECTION_GET_PRIVATE (conn)->in_use = TRUE;
841 }
842
843 /**
844  * soup_connection_release:
845  * @conn: a #SoupConnection
846  *
847  * Marks @conn as not "in use". This can be used to cancel the effect
848  * of a soup_connection_reserve(). It is not necessary to call this
849  * after soup_connection_send_request().
850  **/
851 void
852 soup_connection_release (SoupConnection *conn)
853 {
854         g_return_if_fail (SOUP_IS_CONNECTION (conn));
855
856         clear_current_request (conn);
857 }