New. An interface for objects that want to act on every message passing
[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 <sys/socket.h>
21 #include <netinet/tcp.h>
22 #include <netinet/in.h>
23
24 #include "soup-address.h"
25 #include "soup-connection.h"
26 #include "soup-marshal.h"
27 #include "soup-message.h"
28 #include "soup-message-filter.h"
29 #include "soup-misc.h"
30 #include "soup-socket.h"
31 #include "soup-ssl.h"
32 #include "soup-uri.h"
33
34 struct SoupConnectionPrivate {
35         SoupSocket  *socket;
36
37         /* proxy_uri is the URI of the proxy server we are connected
38          * to, if any. origin_uri is the URI of the origin server.
39          * conn_uri is the uri of the host we are actually directly
40          * connected to, which will be proxy_uri if there's a proxy
41          * and origin_uri if not.
42          */
43         SoupUri     *proxy_uri, *origin_uri, *conn_uri;
44         gpointer     ssl_creds;
45
46         SoupMessageFilter *filter;
47
48         SoupMessage *cur_req;
49         time_t       last_used;
50         gboolean     in_use;
51 };
52
53 #define PARENT_TYPE G_TYPE_OBJECT
54 static GObjectClass *parent_class;
55
56 enum {
57         CONNECT_RESULT,
58         DISCONNECTED,
59         AUTHENTICATE,
60         REAUTHENTICATE,
61         LAST_SIGNAL
62 };
63
64 static guint signals[LAST_SIGNAL] = { 0 };
65
66 enum {
67   PROP_0,
68
69   PROP_ORIGIN_URI,
70   PROP_PROXY_URI,
71   PROP_SSL_CREDS,
72   PROP_MESSAGE_FILTER,
73
74   LAST_PROP
75 };
76
77 static void set_property (GObject *object, guint prop_id,
78                           const GValue *value, GParamSpec *pspec);
79 static void get_property (GObject *object, guint prop_id,
80                           GValue *value, GParamSpec *pspec);
81
82 static void request_done (SoupMessage *req, gpointer user_data);
83 static void send_request (SoupConnection *conn, SoupMessage *req);
84
85 static void
86 init (GObject *object)
87 {
88         SoupConnection *conn = SOUP_CONNECTION (object);
89
90         conn->priv = g_new0 (SoupConnectionPrivate, 1);
91 }
92
93 static void
94 finalize (GObject *object)
95 {
96         SoupConnection *conn = SOUP_CONNECTION (object);
97
98         if (conn->priv->proxy_uri)
99                 soup_uri_free (conn->priv->proxy_uri);
100         if (conn->priv->origin_uri)
101                 soup_uri_free (conn->priv->origin_uri);
102
103         if (conn->priv->filter)
104                 g_object_unref (conn->priv->filter);
105
106         g_free (conn->priv);
107
108         G_OBJECT_CLASS (parent_class)->finalize (object);
109 }
110
111 static void
112 dispose (GObject *object)
113 {
114         SoupConnection *conn = SOUP_CONNECTION (object);
115
116         if (conn->priv->cur_req)
117                 request_done (conn->priv->cur_req, conn);
118         soup_connection_disconnect (conn);
119
120         G_OBJECT_CLASS (parent_class)->dispose (object);
121 }
122
123 static void
124 class_init (GObjectClass *object_class)
125 {
126         SoupConnectionClass *connection_class =
127                 SOUP_CONNECTION_CLASS (object_class);
128
129         parent_class = g_type_class_ref (PARENT_TYPE);
130
131         /* virtual method definition */
132         connection_class->send_request = send_request;
133
134         /* virtual method override */
135         object_class->dispose = dispose;
136         object_class->finalize = finalize;
137         object_class->set_property = set_property;
138         object_class->get_property = get_property;
139
140         /* signals */
141         signals[CONNECT_RESULT] =
142                 g_signal_new ("connect_result",
143                               G_OBJECT_CLASS_TYPE (object_class),
144                               G_SIGNAL_RUN_FIRST,
145                               G_STRUCT_OFFSET (SoupConnectionClass, connect_result),
146                               NULL, NULL,
147                               soup_marshal_NONE__INT,
148                               G_TYPE_NONE, 1,
149                               G_TYPE_INT);
150         signals[DISCONNECTED] =
151                 g_signal_new ("disconnected",
152                               G_OBJECT_CLASS_TYPE (object_class),
153                               G_SIGNAL_RUN_FIRST,
154                               G_STRUCT_OFFSET (SoupConnectionClass, disconnected),
155                               NULL, NULL,
156                               soup_marshal_NONE__NONE,
157                               G_TYPE_NONE, 0);
158         signals[AUTHENTICATE] =
159                 g_signal_new ("authenticate",
160                               G_OBJECT_CLASS_TYPE (object_class),
161                               G_SIGNAL_RUN_FIRST,
162                               G_STRUCT_OFFSET (SoupConnectionClass, authenticate),
163                               NULL, NULL,
164                               soup_marshal_NONE__OBJECT_STRING_STRING_POINTER_POINTER,
165                               G_TYPE_NONE, 5,
166                               SOUP_TYPE_MESSAGE,
167                               G_TYPE_STRING,
168                               G_TYPE_STRING,
169                               G_TYPE_POINTER,
170                               G_TYPE_POINTER);
171         signals[REAUTHENTICATE] =
172                 g_signal_new ("reauthenticate",
173                               G_OBJECT_CLASS_TYPE (object_class),
174                               G_SIGNAL_RUN_FIRST,
175                               G_STRUCT_OFFSET (SoupConnectionClass, reauthenticate),
176                               NULL, NULL,
177                               soup_marshal_NONE__OBJECT_STRING_STRING_POINTER_POINTER,
178                               G_TYPE_NONE, 5,
179                               SOUP_TYPE_MESSAGE,
180                               G_TYPE_STRING,
181                               G_TYPE_STRING,
182                               G_TYPE_POINTER,
183                               G_TYPE_POINTER);
184
185         /* properties */
186         g_object_class_install_property (
187                 object_class, PROP_ORIGIN_URI,
188                 g_param_spec_pointer (SOUP_CONNECTION_ORIGIN_URI,
189                                       "Origin URI",
190                                       "The HTTP origin server to use for this connection",
191                                       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
192         g_object_class_install_property (
193                 object_class, PROP_PROXY_URI,
194                 g_param_spec_pointer (SOUP_CONNECTION_PROXY_URI,
195                                       "Proxy URI",
196                                       "The HTTP Proxy to use for this connection",
197                                       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
198         g_object_class_install_property (
199                 object_class, PROP_SSL_CREDS,
200                 g_param_spec_pointer (SOUP_CONNECTION_SSL_CREDENTIALS,
201                                       "SSL credentials",
202                                       "Opaque SSL credentials for this connection",
203                                       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
204         g_object_class_install_property (
205                 object_class, PROP_MESSAGE_FILTER,
206                 g_param_spec_pointer (SOUP_CONNECTION_MESSAGE_FILTER,
207                                       "Message filter",
208                                       "Message filter object for this connection",
209                                       G_PARAM_READWRITE));
210 }
211
212 SOUP_MAKE_TYPE (soup_connection, SoupConnection, class_init, init, PARENT_TYPE)
213
214
215 /**
216  * soup_connection_new:
217  * @propname1: name of first property to set
218  * @...:
219  *
220  * Creates an HTTP connection. You must set at least one of
221  * %SOUP_CONNECTION_ORIGIN_URI or %SOUP_CONNECTION_PROXY_URI. If you
222  * set an origin server URI but no proxy URI, this will be a direct
223  * connection. If you set a proxy URI and an https origin server URI,
224  * this will be a tunnel. Otherwise it will be an http proxy
225  * connection.
226  *
227  * You must call soup_connection_connect_async() or
228  * soup_connection_connect_sync() to connect it after creating it.
229  *
230  * Return value: the new connection (not yet ready for use).
231  **/
232 SoupConnection *
233 soup_connection_new (const char *propname1, ...)
234 {
235         SoupConnection *conn;
236         va_list ap;
237
238         va_start (ap, propname1);
239         conn = (SoupConnection *)g_object_new_valist (SOUP_TYPE_CONNECTION,
240                                                       propname1, ap);
241         va_end (ap);
242
243         return conn;
244 }
245
246 static void
247 set_property (GObject *object, guint prop_id,
248               const GValue *value, GParamSpec *pspec)
249 {
250         SoupConnection *conn = SOUP_CONNECTION (object);
251         gpointer pval;
252
253         switch (prop_id) {
254         case PROP_ORIGIN_URI:
255                 pval = g_value_get_pointer (value);
256                 conn->priv->origin_uri = pval ? soup_uri_copy (pval) : NULL;
257                 if (!conn->priv->proxy_uri)
258                         conn->priv->conn_uri = conn->priv->origin_uri;
259                 break;
260         case PROP_PROXY_URI:
261                 pval = g_value_get_pointer (value);
262                 conn->priv->proxy_uri = pval ? soup_uri_copy (pval) : NULL;
263                 if (conn->priv->proxy_uri)
264                         conn->priv->conn_uri = conn->priv->proxy_uri;
265                 else
266                         conn->priv->conn_uri = conn->priv->origin_uri;
267                 break;
268         case PROP_SSL_CREDS:
269                 conn->priv->ssl_creds = g_value_get_pointer (value);
270                 break;
271         case PROP_MESSAGE_FILTER:
272                 conn->priv->filter = g_object_ref (g_value_get_pointer (value));
273                 break;
274         default:
275                 break;
276         }
277 }
278
279 static void
280 get_property (GObject *object, guint prop_id,
281               GValue *value, GParamSpec *pspec)
282 {
283         SoupConnection *conn = SOUP_CONNECTION (object);
284
285         switch (prop_id) {
286         case PROP_ORIGIN_URI:
287                 g_value_set_pointer (value, conn->priv->origin_uri ?
288                                      soup_uri_copy (conn->priv->origin_uri) :
289                                      NULL);
290                 break;
291         case PROP_PROXY_URI:
292                 g_value_set_pointer (value, conn->priv->proxy_uri ?
293                                      soup_uri_copy (conn->priv->proxy_uri) :
294                                      NULL);
295         case PROP_SSL_CREDS:
296                 g_value_set_pointer (value, conn->priv->ssl_creds);
297                 break;
298         case PROP_MESSAGE_FILTER:
299                 g_value_set_pointer (value, g_object_ref (conn->priv->filter));
300                 break;
301         default:
302                 break;
303         }
304 }
305
306 static void
307 set_current_request (SoupConnection *conn, SoupMessage *req)
308 {
309         g_return_if_fail (conn->priv->cur_req == NULL);
310
311         req->status = SOUP_MESSAGE_STATUS_RUNNING;
312         conn->priv->cur_req = req;
313         conn->priv->in_use = TRUE;
314         g_object_add_weak_pointer (G_OBJECT (req),
315                                    (gpointer *)conn->priv->cur_req);
316 }
317
318 static void
319 clear_current_request (SoupConnection *conn)
320 {
321         if (conn->priv->cur_req) {
322                 g_object_remove_weak_pointer (G_OBJECT (conn->priv->cur_req),
323                                               (gpointer *)conn->priv->cur_req);
324                 conn->priv->cur_req = NULL;
325         }
326         conn->priv->in_use = FALSE;
327 }
328
329 static void
330 socket_disconnected (SoupSocket *sock, gpointer conn)
331 {
332         soup_connection_disconnect (conn);
333 }
334
335 static inline guint
336 proxified_status (SoupConnection *conn, guint status)
337 {
338         if (!conn->priv->proxy_uri)
339                 return status;
340
341         if (status == SOUP_STATUS_CANT_RESOLVE)
342                 return SOUP_STATUS_CANT_RESOLVE_PROXY;
343         else if (status == SOUP_STATUS_CANT_CONNECT)
344                 return SOUP_STATUS_CANT_CONNECT_PROXY;
345         else
346                 return status;
347 }
348
349 static void
350 tunnel_connect_finished (SoupMessage *msg, gpointer user_data)
351 {
352         SoupConnection *conn = user_data;
353         guint status = msg->status_code;
354
355         clear_current_request (conn);
356
357         if (SOUP_STATUS_IS_SUCCESSFUL (status)) {
358                 if (!soup_socket_start_ssl (conn->priv->socket))
359                         status = SOUP_STATUS_SSL_FAILED;
360         }
361
362         g_signal_emit (conn, signals[CONNECT_RESULT], 0,
363                        proxified_status (conn, status));
364         g_object_unref (msg);
365 }
366
367 static void
368 tunnel_connect_restarted (SoupMessage *msg, gpointer user_data)
369 {
370         SoupConnection *conn = user_data;
371         guint status = msg->status_code;
372
373         /* We only allow one restart: if another one happens, treat
374          * it as "finished".
375          */
376         g_signal_handlers_disconnect_by_func (msg, tunnel_connect_restarted, conn);
377         g_signal_connect (msg, "restarted",
378                           G_CALLBACK (tunnel_connect_finished), conn);
379
380         if (status == SOUP_STATUS_PROXY_AUTHENTICATION_REQUIRED) {
381                 /* Our parent session has handled the authentication
382                  * and attempted to restart the message.
383                  */
384                 if (soup_message_is_keepalive (msg)) {
385                         /* Connection is still open, so just send the
386                          * message again.
387                          */
388                         soup_connection_send_request (conn, msg);
389                 } else {
390                         /* Tell the session to try again. */
391                         soup_message_set_status (msg, SOUP_STATUS_TRY_AGAIN);
392                         soup_message_finished (msg);
393                 }
394         } else
395                 soup_message_finished (msg);
396 }
397
398 static void
399 socket_connect_result (SoupSocket *sock, guint status, gpointer user_data)
400 {
401         SoupConnection *conn = user_data;
402
403         if (!SOUP_STATUS_IS_SUCCESSFUL (status))
404                 goto done;
405
406         if (conn->priv->conn_uri->protocol == SOUP_PROTOCOL_HTTPS) {
407                 if (!soup_socket_start_ssl (sock)) {
408                         status = SOUP_STATUS_SSL_FAILED;
409                         goto done;
410                 }
411         }
412
413         /* See if we need to tunnel */
414         if (conn->priv->proxy_uri &&
415             conn->priv->origin_uri &&
416             conn->priv->origin_uri->protocol == SOUP_PROTOCOL_HTTPS) {
417                 SoupMessage *connect_msg;
418
419                 connect_msg = soup_message_new_from_uri (SOUP_METHOD_CONNECT,
420                                                          conn->priv->origin_uri);
421
422                 g_signal_connect (connect_msg, "restarted",
423                                   G_CALLBACK (tunnel_connect_restarted), conn);
424                 g_signal_connect (connect_msg, "finished",
425                                   G_CALLBACK (tunnel_connect_finished), conn);
426
427                 soup_connection_send_request (conn, connect_msg);
428                 return;
429         }
430
431  done:
432         g_signal_emit (conn, signals[CONNECT_RESULT], 0,
433                        proxified_status (conn, status));
434 }
435
436 /**
437  * soup_connection_connect_async:
438  * @conn: the connection
439  * @callback: callback to call when the connection succeeds or fails
440  * @user_data: data for @callback
441  *
442  * Asynchronously connects @conn.
443  **/
444 void
445 soup_connection_connect_async (SoupConnection *conn,
446                                SoupConnectionCallback callback,
447                                gpointer user_data)
448 {
449         g_return_if_fail (SOUP_IS_CONNECTION (conn));
450         g_return_if_fail (conn->priv->socket == NULL);
451
452         if (callback) {
453                 soup_signal_connect_once (conn, "connect_result",
454                                           G_CALLBACK (callback), user_data);
455         }
456
457         conn->priv->socket =
458                 soup_socket_client_new_async (conn->priv->conn_uri->host,
459                                               conn->priv->conn_uri->port,
460                                               conn->priv->ssl_creds,
461                                               socket_connect_result, conn);
462         g_signal_connect (conn->priv->socket, "disconnected",
463                           G_CALLBACK (socket_disconnected), conn);
464 }
465
466 /**
467  * soup_connection_connect_sync:
468  * @conn: the connection
469  *
470  * Synchronously connects @conn.
471  *
472  * Return value: the soup status
473  **/
474 guint
475 soup_connection_connect_sync (SoupConnection *conn)
476 {
477         guint status;
478
479         g_return_val_if_fail (SOUP_IS_CONNECTION (conn), SOUP_STATUS_MALFORMED);
480         g_return_val_if_fail (conn->priv->socket == NULL, SOUP_STATUS_MALFORMED);
481
482         conn->priv->socket =
483                 soup_socket_client_new_sync (conn->priv->conn_uri->host,
484                                              conn->priv->conn_uri->port,
485                                              conn->priv->ssl_creds,
486                                              &status);
487
488         if (!SOUP_STATUS_IS_SUCCESSFUL (status))
489                 goto fail;
490
491         if (conn->priv->conn_uri->protocol == SOUP_PROTOCOL_HTTPS) {
492                 if (!soup_socket_start_ssl (conn->priv->socket)) {
493                         status = SOUP_STATUS_SSL_FAILED;
494                         goto fail;
495                 }
496         }
497
498         /* See if we need to tunnel */
499         if (conn->priv->proxy_uri &&
500             conn->priv->origin_uri &&
501             conn->priv->origin_uri->protocol == SOUP_PROTOCOL_HTTPS) {
502                 SoupMessage *connect_msg;
503
504                 connect_msg = soup_message_new_from_uri (SOUP_METHOD_CONNECT,
505                                                          conn->priv->origin_uri);
506                 soup_connection_send_request (conn, connect_msg);
507                 status = connect_msg->status_code;
508
509                 if (status == SOUP_STATUS_PROXY_AUTHENTICATION_REQUIRED &&
510                     SOUP_MESSAGE_IS_STARTING (connect_msg)) {
511                         if (soup_message_is_keepalive (connect_msg)) {
512                                 /* Try once more */
513                                 soup_connection_send_request (conn, connect_msg);
514                                 status = connect_msg->status_code;
515                         } else
516                                 status = SOUP_STATUS_TRY_AGAIN;
517                 }
518
519                 g_object_unref (connect_msg);
520         }
521
522         if (!SOUP_STATUS_IS_SUCCESSFUL (status)) {
523         fail:
524                 if (conn->priv->socket) {
525                         g_object_unref (conn->priv->socket);
526                         conn->priv->socket = NULL;
527                 }
528         }
529
530         g_signal_emit (conn, signals[CONNECT_RESULT], 0,
531                        proxified_status (conn, status));
532         return proxified_status (conn, status);
533 }
534
535
536 /**
537  * soup_connection_disconnect:
538  * @conn: a connection
539  *
540  * Disconnects @conn's socket and emits a %disconnected signal.
541  * After calling this, @conn will be essentially useless.
542  **/
543 void
544 soup_connection_disconnect (SoupConnection *conn)
545 {
546         g_return_if_fail (SOUP_IS_CONNECTION (conn));
547
548         if (!conn->priv->socket)
549                 return;
550
551         g_signal_handlers_disconnect_by_func (conn->priv->socket,
552                                               socket_disconnected, conn);
553         soup_socket_disconnect (conn->priv->socket);
554         g_object_unref (conn->priv->socket);
555         conn->priv->socket = NULL;
556         g_signal_emit (conn, signals[DISCONNECTED], 0);
557 }
558
559 /**
560  * soup_connection_is_in_use:
561  * @conn: a connection
562  *
563  * Return value: whether or not @conn is being used.
564  **/
565 gboolean
566 soup_connection_is_in_use (SoupConnection *conn)
567 {
568         g_return_val_if_fail (SOUP_IS_CONNECTION (conn), FALSE);
569
570         return conn->priv->in_use;
571 }
572
573 /**
574  * soup_connection_last_used:
575  * @conn: a #SoupConnection.
576  *
577  * Return value: the last time a response was received on @conn, or 0
578  * if @conn has not been used yet.
579  */
580 time_t
581 soup_connection_last_used (SoupConnection *conn)
582 {
583         g_return_val_if_fail (SOUP_IS_CONNECTION (conn), FALSE);
584
585         return conn->priv->last_used;
586 }
587
588 static void
589 request_restarted (SoupMessage *req, gpointer conn)
590 {
591         if (!soup_message_is_keepalive (req))
592                 soup_connection_disconnect (conn);
593 }
594
595 static void
596 request_done (SoupMessage *req, gpointer user_data)
597 {
598         SoupConnection *conn = user_data;
599
600         clear_current_request (conn);
601         conn->priv->last_used = time (NULL);
602
603         if (!soup_message_is_keepalive (req))
604                 soup_connection_disconnect (conn);
605
606         g_signal_handlers_disconnect_by_func (req, request_done, conn);
607         g_signal_handlers_disconnect_by_func (req, request_restarted, conn);
608         g_object_unref (conn);
609 }
610
611 static void
612 send_request (SoupConnection *conn, SoupMessage *req)
613 {
614         g_object_ref (conn);
615
616         if (req != conn->priv->cur_req) {
617                 set_current_request (conn, req);
618
619                 g_signal_connect (req, "restarted",
620                                   G_CALLBACK (request_restarted), conn);
621                 g_signal_connect (req, "finished",
622                                   G_CALLBACK (request_done), conn);
623
624                 if (conn->priv->filter)
625                         soup_message_filter_setup_message (conn->priv->filter, req);
626         }
627
628         soup_message_io_cancel (req);
629         soup_message_send_request (req, conn->priv->socket,
630                                    conn->priv->proxy_uri != NULL);
631 }
632
633 void
634 soup_connection_send_request (SoupConnection *conn, SoupMessage *req)
635 {
636         g_return_if_fail (SOUP_IS_CONNECTION (conn));
637         g_return_if_fail (SOUP_IS_MESSAGE (req));
638         g_return_if_fail (conn->priv->socket != NULL);
639
640         SOUP_CONNECTION_GET_CLASS (conn)->send_request (conn, req);
641 }
642
643 void
644 soup_connection_reserve (SoupConnection *conn)
645 {
646         g_return_if_fail (SOUP_IS_CONNECTION (conn));
647
648         conn->priv->in_use = TRUE;
649 }
650
651 void
652 soup_connection_authenticate (SoupConnection *conn, SoupMessage *msg,
653                               const char *auth_type, const char *auth_realm,
654                               char **username, char **password)
655 {
656         g_signal_emit (conn, signals[AUTHENTICATE], 0,
657                        msg, auth_type, auth_realm, username, password);
658 }
659
660 void
661 soup_connection_reauthenticate (SoupConnection *conn, SoupMessage *msg,
662                                 const char *auth_type, const char *auth_realm,
663                                 char **username, char **password)
664 {
665         g_signal_emit (conn, signals[REAUTHENTICATE], 0,
666                        msg, auth_type, auth_realm, username, password);
667 }