1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * soup-connection.c: A single HTTP/HTTPS connection
5 * Copyright (C) 2000-2003, Ximian, Inc.
18 #include <sys/types.h>
20 #include <sys/socket.h>
21 #include <netinet/tcp.h>
22 #include <netinet/in.h>
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"
34 struct SoupConnectionPrivate {
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.
43 SoupUri *proxy_uri, *origin_uri, *conn_uri;
46 SoupMessageFilter *filter;
53 #define PARENT_TYPE G_TYPE_OBJECT
54 static GObjectClass *parent_class;
64 static guint signals[LAST_SIGNAL] = { 0 };
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);
82 static void request_done (SoupMessage *req, gpointer user_data);
83 static void send_request (SoupConnection *conn, SoupMessage *req);
86 init (GObject *object)
88 SoupConnection *conn = SOUP_CONNECTION (object);
90 conn->priv = g_new0 (SoupConnectionPrivate, 1);
94 finalize (GObject *object)
96 SoupConnection *conn = SOUP_CONNECTION (object);
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);
103 if (conn->priv->filter)
104 g_object_unref (conn->priv->filter);
108 G_OBJECT_CLASS (parent_class)->finalize (object);
112 dispose (GObject *object)
114 SoupConnection *conn = SOUP_CONNECTION (object);
116 if (conn->priv->cur_req)
117 request_done (conn->priv->cur_req, conn);
118 soup_connection_disconnect (conn);
120 G_OBJECT_CLASS (parent_class)->dispose (object);
124 class_init (GObjectClass *object_class)
126 SoupConnectionClass *connection_class =
127 SOUP_CONNECTION_CLASS (object_class);
129 parent_class = g_type_class_ref (PARENT_TYPE);
131 /* virtual method definition */
132 connection_class->send_request = send_request;
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;
141 signals[CONNECT_RESULT] =
142 g_signal_new ("connect_result",
143 G_OBJECT_CLASS_TYPE (object_class),
145 G_STRUCT_OFFSET (SoupConnectionClass, connect_result),
147 soup_marshal_NONE__INT,
150 signals[DISCONNECTED] =
151 g_signal_new ("disconnected",
152 G_OBJECT_CLASS_TYPE (object_class),
154 G_STRUCT_OFFSET (SoupConnectionClass, disconnected),
156 soup_marshal_NONE__NONE,
158 signals[AUTHENTICATE] =
159 g_signal_new ("authenticate",
160 G_OBJECT_CLASS_TYPE (object_class),
162 G_STRUCT_OFFSET (SoupConnectionClass, authenticate),
164 soup_marshal_NONE__OBJECT_STRING_STRING_POINTER_POINTER,
171 signals[REAUTHENTICATE] =
172 g_signal_new ("reauthenticate",
173 G_OBJECT_CLASS_TYPE (object_class),
175 G_STRUCT_OFFSET (SoupConnectionClass, reauthenticate),
177 soup_marshal_NONE__OBJECT_STRING_STRING_POINTER_POINTER,
186 g_object_class_install_property (
187 object_class, PROP_ORIGIN_URI,
188 g_param_spec_pointer (SOUP_CONNECTION_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,
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,
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,
208 "Message filter object for this connection",
212 SOUP_MAKE_TYPE (soup_connection, SoupConnection, class_init, init, PARENT_TYPE)
216 * soup_connection_new:
217 * @propname1: name of first property to set
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
227 * You must call soup_connection_connect_async() or
228 * soup_connection_connect_sync() to connect it after creating it.
230 * Return value: the new connection (not yet ready for use).
233 soup_connection_new (const char *propname1, ...)
235 SoupConnection *conn;
238 va_start (ap, propname1);
239 conn = (SoupConnection *)g_object_new_valist (SOUP_TYPE_CONNECTION,
247 set_property (GObject *object, guint prop_id,
248 const GValue *value, GParamSpec *pspec)
250 SoupConnection *conn = SOUP_CONNECTION (object);
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;
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;
266 conn->priv->conn_uri = conn->priv->origin_uri;
269 conn->priv->ssl_creds = g_value_get_pointer (value);
271 case PROP_MESSAGE_FILTER:
272 conn->priv->filter = g_object_ref (g_value_get_pointer (value));
280 get_property (GObject *object, guint prop_id,
281 GValue *value, GParamSpec *pspec)
283 SoupConnection *conn = SOUP_CONNECTION (object);
286 case PROP_ORIGIN_URI:
287 g_value_set_pointer (value, conn->priv->origin_uri ?
288 soup_uri_copy (conn->priv->origin_uri) :
292 g_value_set_pointer (value, conn->priv->proxy_uri ?
293 soup_uri_copy (conn->priv->proxy_uri) :
296 g_value_set_pointer (value, conn->priv->ssl_creds);
298 case PROP_MESSAGE_FILTER:
299 g_value_set_pointer (value, g_object_ref (conn->priv->filter));
307 set_current_request (SoupConnection *conn, SoupMessage *req)
309 g_return_if_fail (conn->priv->cur_req == NULL);
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);
319 clear_current_request (SoupConnection *conn)
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;
326 conn->priv->in_use = FALSE;
330 socket_disconnected (SoupSocket *sock, gpointer conn)
332 soup_connection_disconnect (conn);
336 proxified_status (SoupConnection *conn, guint status)
338 if (!conn->priv->proxy_uri)
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;
350 tunnel_connect_finished (SoupMessage *msg, gpointer user_data)
352 SoupConnection *conn = user_data;
353 guint status = msg->status_code;
355 clear_current_request (conn);
357 if (SOUP_STATUS_IS_SUCCESSFUL (status)) {
358 if (!soup_socket_start_ssl (conn->priv->socket))
359 status = SOUP_STATUS_SSL_FAILED;
362 g_signal_emit (conn, signals[CONNECT_RESULT], 0,
363 proxified_status (conn, status));
364 g_object_unref (msg);
368 tunnel_connect_restarted (SoupMessage *msg, gpointer user_data)
370 SoupConnection *conn = user_data;
371 guint status = msg->status_code;
373 /* We only allow one restart: if another one happens, treat
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);
380 if (status == SOUP_STATUS_PROXY_AUTHENTICATION_REQUIRED) {
381 /* Our parent session has handled the authentication
382 * and attempted to restart the message.
384 if (soup_message_is_keepalive (msg)) {
385 /* Connection is still open, so just send the
388 soup_connection_send_request (conn, msg);
390 /* Tell the session to try again. */
391 soup_message_set_status (msg, SOUP_STATUS_TRY_AGAIN);
392 soup_message_finished (msg);
395 soup_message_finished (msg);
399 socket_connect_result (SoupSocket *sock, guint status, gpointer user_data)
401 SoupConnection *conn = user_data;
403 if (!SOUP_STATUS_IS_SUCCESSFUL (status))
406 if (conn->priv->conn_uri->protocol == SOUP_PROTOCOL_HTTPS) {
407 if (!soup_socket_start_ssl (sock)) {
408 status = SOUP_STATUS_SSL_FAILED;
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;
419 connect_msg = soup_message_new_from_uri (SOUP_METHOD_CONNECT,
420 conn->priv->origin_uri);
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);
427 soup_connection_send_request (conn, connect_msg);
432 g_signal_emit (conn, signals[CONNECT_RESULT], 0,
433 proxified_status (conn, status));
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
442 * Asynchronously connects @conn.
445 soup_connection_connect_async (SoupConnection *conn,
446 SoupConnectionCallback callback,
449 g_return_if_fail (SOUP_IS_CONNECTION (conn));
450 g_return_if_fail (conn->priv->socket == NULL);
453 soup_signal_connect_once (conn, "connect_result",
454 G_CALLBACK (callback), user_data);
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);
467 * soup_connection_connect_sync:
468 * @conn: the connection
470 * Synchronously connects @conn.
472 * Return value: the soup status
475 soup_connection_connect_sync (SoupConnection *conn)
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);
483 soup_socket_client_new_sync (conn->priv->conn_uri->host,
484 conn->priv->conn_uri->port,
485 conn->priv->ssl_creds,
488 if (!SOUP_STATUS_IS_SUCCESSFUL (status))
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;
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;
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;
509 if (status == SOUP_STATUS_PROXY_AUTHENTICATION_REQUIRED &&
510 SOUP_MESSAGE_IS_STARTING (connect_msg)) {
511 if (soup_message_is_keepalive (connect_msg)) {
513 soup_connection_send_request (conn, connect_msg);
514 status = connect_msg->status_code;
516 status = SOUP_STATUS_TRY_AGAIN;
519 g_object_unref (connect_msg);
522 if (!SOUP_STATUS_IS_SUCCESSFUL (status)) {
524 if (conn->priv->socket) {
525 g_object_unref (conn->priv->socket);
526 conn->priv->socket = NULL;
530 g_signal_emit (conn, signals[CONNECT_RESULT], 0,
531 proxified_status (conn, status));
532 return proxified_status (conn, status);
537 * soup_connection_disconnect:
538 * @conn: a connection
540 * Disconnects @conn's socket and emits a %disconnected signal.
541 * After calling this, @conn will be essentially useless.
544 soup_connection_disconnect (SoupConnection *conn)
546 g_return_if_fail (SOUP_IS_CONNECTION (conn));
548 if (!conn->priv->socket)
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);
560 * soup_connection_is_in_use:
561 * @conn: a connection
563 * Return value: whether or not @conn is being used.
566 soup_connection_is_in_use (SoupConnection *conn)
568 g_return_val_if_fail (SOUP_IS_CONNECTION (conn), FALSE);
570 return conn->priv->in_use;
574 * soup_connection_last_used:
575 * @conn: a #SoupConnection.
577 * Return value: the last time a response was received on @conn, or 0
578 * if @conn has not been used yet.
581 soup_connection_last_used (SoupConnection *conn)
583 g_return_val_if_fail (SOUP_IS_CONNECTION (conn), FALSE);
585 return conn->priv->last_used;
589 request_restarted (SoupMessage *req, gpointer conn)
591 if (!soup_message_is_keepalive (req))
592 soup_connection_disconnect (conn);
596 request_done (SoupMessage *req, gpointer user_data)
598 SoupConnection *conn = user_data;
600 clear_current_request (conn);
601 conn->priv->last_used = time (NULL);
603 if (!soup_message_is_keepalive (req))
604 soup_connection_disconnect (conn);
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);
612 send_request (SoupConnection *conn, SoupMessage *req)
616 if (req != conn->priv->cur_req) {
617 set_current_request (conn, req);
619 g_signal_connect (req, "restarted",
620 G_CALLBACK (request_restarted), conn);
621 g_signal_connect (req, "finished",
622 G_CALLBACK (request_done), conn);
624 if (conn->priv->filter)
625 soup_message_filter_setup_message (conn->priv->filter, req);
628 soup_message_io_cancel (req);
629 soup_message_send_request (req, conn->priv->socket,
630 conn->priv->proxy_uri != NULL);
634 soup_connection_send_request (SoupConnection *conn, SoupMessage *req)
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);
640 SOUP_CONNECTION_GET_CLASS (conn)->send_request (conn, req);
644 soup_connection_reserve (SoupConnection *conn)
646 g_return_if_fail (SOUP_IS_CONNECTION (conn));
648 conn->priv->in_use = TRUE;
652 soup_connection_authenticate (SoupConnection *conn, SoupMessage *msg,
653 const char *auth_type, const char *auth_realm,
654 char **username, char **password)
656 g_signal_emit (conn, signals[AUTHENTICATE], 0,
657 msg, auth_type, auth_realm, username, password);
661 soup_connection_reauthenticate (SoupConnection *conn, SoupMessage *msg,
662 const char *auth_type, const char *auth_realm,
663 char **username, char **password)
665 g_signal_emit (conn, signals[REAUTHENTICATE], 0,
666 msg, auth_type, auth_realm, username, password);