• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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-message-queue.h"
15 #include "soup-socket-private.h"
16 #include "soup-misc-private.h"
17 
18 typedef struct {
19 	SoupSocket  *socket;
20 	SoupSocketProperties *socket_props;
21 
22 	SoupURI *remote_uri, *proxy_uri;
23 	gboolean ssl;
24 
25 	SoupMessage *current_msg;
26 	SoupConnectionState state;
27 	time_t       unused_timeout;
28 	GSource     *idle_timeout_src;
29 	gboolean     reusable;
30 } SoupConnectionPrivate;
31 
32 G_DEFINE_TYPE_WITH_PRIVATE (SoupConnection, soup_connection, G_TYPE_OBJECT)
33 
34 enum {
35 	EVENT,
36 	DISCONNECTED,
37 	LAST_SIGNAL
38 };
39 
40 static guint signals[LAST_SIGNAL] = { 0 };
41 
42 enum {
43 	PROP_0,
44 
45 	PROP_REMOTE_URI,
46 	PROP_SOCKET_PROPERTIES,
47 	PROP_STATE,
48 	PROP_SSL,
49 
50 	LAST_PROP
51 };
52 
53 static void stop_idle_timer (SoupConnectionPrivate *priv);
54 
55 /* Number of seconds after which we close a connection that hasn't yet
56  * been used.
57  */
58 #define SOUP_CONNECTION_UNUSED_TIMEOUT 3
59 
60 static void
soup_connection_init(SoupConnection * conn)61 soup_connection_init (SoupConnection *conn)
62 {
63 }
64 
65 static void
soup_connection_finalize(GObject * object)66 soup_connection_finalize (GObject *object)
67 {
68 	SoupConnectionPrivate *priv = soup_connection_get_instance_private (SOUP_CONNECTION (object));
69 
70 	g_clear_pointer (&priv->remote_uri, soup_uri_free);
71 	g_clear_pointer (&priv->proxy_uri, soup_uri_free);
72 	g_clear_pointer (&priv->socket_props, soup_socket_properties_unref);
73 	g_clear_object (&priv->current_msg);
74 
75 	if (priv->socket) {
76 		g_signal_handlers_disconnect_by_data (priv->socket, object);
77 		g_object_unref (priv->socket);
78 	}
79 
80 	G_OBJECT_CLASS (soup_connection_parent_class)->finalize (object);
81 }
82 
83 static void
soup_connection_dispose(GObject * object)84 soup_connection_dispose (GObject *object)
85 {
86 	SoupConnection *conn = SOUP_CONNECTION (object);
87 	SoupConnectionPrivate *priv = soup_connection_get_instance_private (conn);
88 
89 	stop_idle_timer (priv);
90 
91 	G_OBJECT_CLASS (soup_connection_parent_class)->dispose (object);
92 }
93 
94 static void
soup_connection_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)95 soup_connection_set_property (GObject *object, guint prop_id,
96 			      const GValue *value, GParamSpec *pspec)
97 {
98 	SoupConnectionPrivate *priv = soup_connection_get_instance_private (SOUP_CONNECTION (object));
99 
100 	switch (prop_id) {
101 	case PROP_REMOTE_URI:
102 		priv->remote_uri = g_value_dup_boxed (value);
103 		break;
104 	case PROP_SOCKET_PROPERTIES:
105 		priv->socket_props = g_value_dup_boxed (value);
106 		break;
107 	case PROP_STATE:
108 		soup_connection_set_state (SOUP_CONNECTION (object), g_value_get_uint (value));
109 		break;
110 	case PROP_SSL:
111 		priv->ssl = g_value_get_boolean (value);
112 		break;
113 	default:
114 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
115 		break;
116 	}
117 }
118 
119 static void
soup_connection_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)120 soup_connection_get_property (GObject *object, guint prop_id,
121 			      GValue *value, GParamSpec *pspec)
122 {
123 	SoupConnectionPrivate *priv = soup_connection_get_instance_private (SOUP_CONNECTION (object));
124 
125 	switch (prop_id) {
126 	case PROP_REMOTE_URI:
127 		g_value_set_boxed (value, priv->remote_uri);
128 		break;
129 	case PROP_SOCKET_PROPERTIES:
130 		g_value_set_boxed (value, priv->socket_props);
131 		break;
132 	case PROP_STATE:
133 		g_value_set_enum (value, priv->state);
134 		break;
135 	case PROP_SSL:
136 		g_value_set_boolean (value, priv->ssl);
137 		break;
138 	default:
139 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
140 		break;
141 	}
142 }
143 
144 static void
soup_connection_class_init(SoupConnectionClass * connection_class)145 soup_connection_class_init (SoupConnectionClass *connection_class)
146 {
147 	GObjectClass *object_class = G_OBJECT_CLASS (connection_class);
148 
149 	/* virtual method override */
150 	object_class->dispose = soup_connection_dispose;
151 	object_class->finalize = soup_connection_finalize;
152 	object_class->set_property = soup_connection_set_property;
153 	object_class->get_property = soup_connection_get_property;
154 
155 	/* signals */
156 	signals[EVENT] =
157 		g_signal_new ("event",
158 			      G_OBJECT_CLASS_TYPE (object_class),
159 			      G_SIGNAL_RUN_FIRST,
160 			      0,
161 			      NULL, NULL,
162 			      NULL,
163 			      G_TYPE_NONE, 2,
164 			      G_TYPE_SOCKET_CLIENT_EVENT,
165 			      G_TYPE_IO_STREAM);
166 	signals[DISCONNECTED] =
167 		g_signal_new ("disconnected",
168 			      G_OBJECT_CLASS_TYPE (object_class),
169 			      G_SIGNAL_RUN_FIRST,
170 			      G_STRUCT_OFFSET (SoupConnectionClass, disconnected),
171 			      NULL, NULL,
172 			      NULL,
173 			      G_TYPE_NONE, 0);
174 
175 	/* properties */
176 	g_object_class_install_property (
177 		object_class, PROP_REMOTE_URI,
178 		g_param_spec_boxed (SOUP_CONNECTION_REMOTE_URI,
179 				    "Remote URI",
180 				    "The URI of the HTTP server",
181 				    SOUP_TYPE_URI,
182 				    G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
183 				    G_PARAM_STATIC_STRINGS));
184 	g_object_class_install_property (
185 		object_class, PROP_SOCKET_PROPERTIES,
186 		g_param_spec_boxed (SOUP_CONNECTION_SOCKET_PROPERTIES,
187 				    "Socket properties",
188 				    "Socket properties",
189 				    SOUP_TYPE_SOCKET_PROPERTIES,
190 				    G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
191 				    G_PARAM_STATIC_STRINGS));
192 	g_object_class_install_property (
193 		object_class, PROP_STATE,
194 		g_param_spec_enum (SOUP_CONNECTION_STATE,
195 				   "Connection state",
196 				   "Current state of connection",
197 				   SOUP_TYPE_CONNECTION_STATE, SOUP_CONNECTION_NEW,
198 				   G_PARAM_READWRITE |
199 				   G_PARAM_STATIC_STRINGS));
200 	g_object_class_install_property (
201 		object_class, PROP_SSL,
202 		g_param_spec_boolean (SOUP_CONNECTION_SSL,
203 				      "Connection uses TLS",
204 				      "Whether the connection should use TLS",
205 				      FALSE,G_PARAM_READWRITE |
206 				      G_PARAM_STATIC_STRINGS));
207 }
208 
209 static void
soup_connection_event(SoupConnection * conn,GSocketClientEvent event,GIOStream * connection)210 soup_connection_event (SoupConnection      *conn,
211 		       GSocketClientEvent   event,
212 		       GIOStream           *connection)
213 {
214 	SoupConnectionPrivate *priv = soup_connection_get_instance_private (conn);
215 
216 	if (!connection && priv->socket)
217 		connection = soup_socket_get_connection (priv->socket);
218 
219 	g_signal_emit (conn, signals[EVENT], 0,
220 		       event, connection);
221 }
222 
223 static gboolean
idle_timeout(gpointer conn)224 idle_timeout (gpointer conn)
225 {
226 	soup_connection_disconnect (conn);
227 	return FALSE;
228 }
229 
230 static void
start_idle_timer(SoupConnection * conn)231 start_idle_timer (SoupConnection *conn)
232 {
233 	SoupConnectionPrivate *priv = soup_connection_get_instance_private (conn);
234 
235 	if (priv->socket_props->idle_timeout > 0 && !priv->idle_timeout_src) {
236 		priv->idle_timeout_src =
237 			soup_add_timeout (priv->socket_props->async_context,
238 					  priv->socket_props->idle_timeout * 1000,
239 					  idle_timeout, conn);
240 	}
241 }
242 
243 static void
stop_idle_timer(SoupConnectionPrivate * priv)244 stop_idle_timer (SoupConnectionPrivate *priv)
245 {
246 	if (priv->idle_timeout_src) {
247 		g_source_destroy (priv->idle_timeout_src);
248 		priv->idle_timeout_src = NULL;
249 	}
250 }
251 
252 static void
current_msg_got_body(SoupMessage * msg,gpointer user_data)253 current_msg_got_body (SoupMessage *msg, gpointer user_data)
254 {
255 	SoupConnection *conn = user_data;
256 	SoupConnectionPrivate *priv = soup_connection_get_instance_private (conn);
257 
258 	priv->unused_timeout = 0;
259 
260 	if (priv->proxy_uri &&
261 	    msg->method == SOUP_METHOD_CONNECT &&
262 	    SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
263 		soup_connection_event (conn, G_SOCKET_CLIENT_PROXY_NEGOTIATED, NULL);
264 
265 		/* We're now effectively no longer proxying */
266 		g_clear_pointer (&priv->proxy_uri, soup_uri_free);
267 	}
268 
269 	priv->reusable = soup_message_is_keepalive (msg);
270 }
271 
272 static void
clear_current_msg(SoupConnection * conn)273 clear_current_msg (SoupConnection *conn)
274 {
275 	SoupConnectionPrivate *priv = soup_connection_get_instance_private (conn);
276 	SoupMessage *msg;
277 
278 	msg = priv->current_msg;
279 	priv->current_msg = NULL;
280 
281 	g_signal_handlers_disconnect_by_func (msg, G_CALLBACK (current_msg_got_body), conn);
282 	g_object_unref (msg);
283 }
284 
285 static void
set_current_msg(SoupConnection * conn,SoupMessage * msg)286 set_current_msg (SoupConnection *conn, SoupMessage *msg)
287 {
288 	SoupConnectionPrivate *priv = soup_connection_get_instance_private (conn);
289 
290 	g_return_if_fail (priv->state == SOUP_CONNECTION_IN_USE);
291 
292 	g_object_freeze_notify (G_OBJECT (conn));
293 
294 	if (priv->current_msg) {
295 		g_return_if_fail (priv->current_msg->method == SOUP_METHOD_CONNECT);
296 		clear_current_msg (conn);
297 	}
298 
299 	stop_idle_timer (priv);
300 
301 	priv->current_msg = g_object_ref (msg);
302 	priv->reusable = FALSE;
303 
304 	g_signal_connect (msg, "got-body",
305 			  G_CALLBACK (current_msg_got_body), conn);
306 
307 	if (priv->proxy_uri && msg->method == SOUP_METHOD_CONNECT)
308 		soup_connection_event (conn, G_SOCKET_CLIENT_PROXY_NEGOTIATING, NULL);
309 
310 	g_object_thaw_notify (G_OBJECT (conn));
311 }
312 
313 static void
re_emit_socket_event(SoupSocket * socket,GSocketClientEvent event,GIOStream * connection,gpointer user_data)314 re_emit_socket_event (SoupSocket          *socket,
315 		      GSocketClientEvent   event,
316 		      GIOStream           *connection,
317 		      gpointer             user_data)
318 {
319 	SoupConnection *conn = user_data;
320 
321 	/* We handle COMPLETE ourselves */
322 	if (event != G_SOCKET_CLIENT_COMPLETE)
323 		soup_connection_event (conn, event, connection);
324 }
325 
326 static void
socket_connect_finished(GTask * task,SoupSocket * sock,GError * error)327 socket_connect_finished (GTask *task, SoupSocket *sock, GError *error)
328 {
329 	SoupConnection *conn = g_task_get_source_object (task);
330 	SoupConnectionPrivate *priv = soup_connection_get_instance_private (conn);
331 
332 	if (!error) {
333 		if (!priv->ssl || !priv->proxy_uri) {
334 			soup_connection_event (conn,
335 					       G_SOCKET_CLIENT_COMPLETE,
336 					       NULL);
337 		}
338 
339 		soup_connection_set_state (conn, SOUP_CONNECTION_IN_USE);
340 		priv->unused_timeout = time (NULL) + SOUP_CONNECTION_UNUSED_TIMEOUT;
341 		start_idle_timer (conn);
342 
343 		g_task_return_boolean (task, TRUE);
344 	} else
345 		g_task_return_error (task, error);
346 	g_object_unref (task);
347 }
348 
349 static void
socket_handshake_complete(GObject * object,GAsyncResult * result,gpointer user_data)350 socket_handshake_complete (GObject *object, GAsyncResult *result, gpointer user_data)
351 {
352 	SoupSocket *sock = SOUP_SOCKET (object);
353 	GTask *task = user_data;
354 	GError *error = NULL;
355 
356 	soup_socket_handshake_finish (sock, result, &error);
357 	socket_connect_finished (task, sock, error);
358 }
359 
360 static void
socket_connect_complete(GObject * object,GAsyncResult * result,gpointer user_data)361 socket_connect_complete (GObject *object, GAsyncResult *result, gpointer user_data)
362 {
363 	SoupSocket *sock = SOUP_SOCKET (object);
364 	GTask *task = user_data;
365 	SoupConnection *conn = g_task_get_source_object (task);
366 	SoupConnectionPrivate *priv = soup_connection_get_instance_private (conn);
367 	GError *error = NULL;
368 
369 	if (!soup_socket_connect_finish_internal (sock, result, &error)) {
370 		socket_connect_finished (task, sock, error);
371 		return;
372 	}
373 
374 	priv->proxy_uri = soup_socket_get_http_proxy_uri (sock);
375 
376 	if (priv->ssl && !priv->proxy_uri) {
377 		soup_socket_handshake_async (sock, priv->remote_uri->host,
378 					     g_task_get_cancellable (task),
379 					     socket_handshake_complete, task);
380 		return;
381 	}
382 
383 	socket_connect_finished (task, sock, NULL);
384 }
385 
386 void
soup_connection_connect_async(SoupConnection * conn,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)387 soup_connection_connect_async (SoupConnection      *conn,
388 			       GCancellable        *cancellable,
389 			       GAsyncReadyCallback  callback,
390 			       gpointer             user_data)
391 {
392 	SoupConnectionPrivate *priv;
393 	SoupAddress *remote_addr;
394 	GTask *task;
395 
396 	g_return_if_fail (SOUP_IS_CONNECTION (conn));
397 	priv = soup_connection_get_instance_private (conn);
398 	g_return_if_fail (priv->socket == NULL);
399 
400 	soup_connection_set_state (conn, SOUP_CONNECTION_CONNECTING);
401 
402 	/* Set the protocol to ensure correct proxy resolution. */
403 	remote_addr =
404 		g_object_new (SOUP_TYPE_ADDRESS,
405 			      SOUP_ADDRESS_NAME, priv->remote_uri->host,
406 			      SOUP_ADDRESS_PORT, priv->remote_uri->port,
407 			      SOUP_ADDRESS_PROTOCOL, priv->remote_uri->scheme,
408 			      NULL);
409 
410 	priv->socket =
411 		soup_socket_new (SOUP_SOCKET_REMOTE_ADDRESS, remote_addr,
412 				 SOUP_SOCKET_SOCKET_PROPERTIES, priv->socket_props,
413 				 NULL);
414 	g_object_unref (remote_addr);
415 
416 	g_signal_connect (priv->socket, "event",
417 			  G_CALLBACK (re_emit_socket_event), conn);
418 
419 	soup_socket_properties_push_async_context (priv->socket_props);
420 	task = g_task_new (conn, cancellable, callback, user_data);
421 
422 	soup_socket_connect_async_internal (priv->socket, cancellable,
423 					    socket_connect_complete, task);
424 	soup_socket_properties_pop_async_context (priv->socket_props);
425 }
426 
427 gboolean
soup_connection_connect_finish(SoupConnection * conn,GAsyncResult * result,GError ** error)428 soup_connection_connect_finish (SoupConnection  *conn,
429 				GAsyncResult    *result,
430 				GError         **error)
431 {
432 	return g_task_propagate_boolean (G_TASK (result), error);
433 }
434 
435 gboolean
soup_connection_connect_sync(SoupConnection * conn,GCancellable * cancellable,GError ** error)436 soup_connection_connect_sync (SoupConnection  *conn,
437 			      GCancellable    *cancellable,
438 			      GError         **error)
439 {
440 	SoupConnectionPrivate *priv;
441 	SoupAddress *remote_addr;
442 
443 	g_return_val_if_fail (SOUP_IS_CONNECTION (conn), FALSE);
444 	priv = soup_connection_get_instance_private (conn);
445 	g_return_val_if_fail (priv->socket == NULL, FALSE);
446 
447 	soup_connection_set_state (conn, SOUP_CONNECTION_CONNECTING);
448 
449 	/* Set the protocol to ensure correct proxy resolution. */
450 	remote_addr =
451 		g_object_new (SOUP_TYPE_ADDRESS,
452 			      SOUP_ADDRESS_NAME, priv->remote_uri->host,
453 			      SOUP_ADDRESS_PORT, priv->remote_uri->port,
454 			      SOUP_ADDRESS_PROTOCOL, priv->remote_uri->scheme,
455 			      NULL);
456 
457 	priv->socket =
458 		soup_socket_new (SOUP_SOCKET_REMOTE_ADDRESS, remote_addr,
459 				 SOUP_SOCKET_SOCKET_PROPERTIES, priv->socket_props,
460 				 SOUP_SOCKET_FLAG_NONBLOCKING, FALSE,
461 				 NULL);
462 	g_object_unref (remote_addr);
463 
464 	g_signal_connect (priv->socket, "event",
465 			  G_CALLBACK (re_emit_socket_event), conn);
466 	if (!soup_socket_connect_sync_internal (priv->socket, cancellable, error))
467 		return FALSE;
468 
469 	priv->proxy_uri = soup_socket_get_http_proxy_uri (priv->socket);
470 
471 	if (priv->ssl && !priv->proxy_uri) {
472 		if (!soup_socket_handshake_sync (priv->socket,
473 						 priv->remote_uri->host,
474 						 cancellable, error))
475 			return FALSE;
476 	}
477 
478 	if (!priv->ssl || !priv->proxy_uri) {
479 		soup_connection_event (conn,
480 				       G_SOCKET_CLIENT_COMPLETE,
481 				       NULL);
482 	}
483 	soup_connection_set_state (conn, SOUP_CONNECTION_IN_USE);
484 	priv->unused_timeout = time (NULL) + SOUP_CONNECTION_UNUSED_TIMEOUT;
485 	start_idle_timer (conn);
486 
487 	return TRUE;
488 }
489 
490 gboolean
soup_connection_is_tunnelled(SoupConnection * conn)491 soup_connection_is_tunnelled (SoupConnection *conn)
492 {
493 	SoupConnectionPrivate *priv;
494 
495 	g_return_val_if_fail (SOUP_IS_CONNECTION (conn), FALSE);
496 	priv = soup_connection_get_instance_private (conn);
497 
498 	return priv->ssl && priv->proxy_uri != NULL;
499 }
500 
501 gboolean
soup_connection_start_ssl_sync(SoupConnection * conn,GCancellable * cancellable,GError ** error)502 soup_connection_start_ssl_sync (SoupConnection  *conn,
503 				GCancellable    *cancellable,
504 				GError         **error)
505 {
506 	SoupConnectionPrivate *priv;
507 
508 	g_return_val_if_fail (SOUP_IS_CONNECTION (conn), FALSE);
509 	priv = soup_connection_get_instance_private (conn);
510 
511 	if (soup_socket_handshake_sync (priv->socket, priv->remote_uri->host,
512 					cancellable, error)) {
513 		soup_connection_event (conn, G_SOCKET_CLIENT_COMPLETE, NULL);
514 		return TRUE;
515 	} else
516 		return FALSE;
517 }
518 
519 static void
start_ssl_completed(GObject * object,GAsyncResult * result,gpointer user_data)520 start_ssl_completed (GObject *object, GAsyncResult *result, gpointer user_data)
521 {
522 	GTask *task = user_data;
523 	SoupConnection *conn = g_task_get_source_object (task);
524 	SoupConnectionPrivate *priv = soup_connection_get_instance_private (conn);
525 	GError *error = NULL;
526 
527 	if (soup_socket_handshake_finish (priv->socket, result, &error)) {
528 		soup_connection_event (conn, G_SOCKET_CLIENT_COMPLETE, NULL);
529 		g_task_return_boolean (task, TRUE);
530 	} else
531 		g_task_return_error (task, error);
532 	g_object_unref (task);
533 }
534 
535 void
soup_connection_start_ssl_async(SoupConnection * conn,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)536 soup_connection_start_ssl_async (SoupConnection      *conn,
537 				 GCancellable        *cancellable,
538 				 GAsyncReadyCallback  callback,
539 				 gpointer             user_data)
540 {
541 	SoupConnectionPrivate *priv;
542 	GTask *task;
543 
544 	g_return_if_fail (SOUP_IS_CONNECTION (conn));
545 	priv = soup_connection_get_instance_private (conn);
546 
547 	soup_socket_properties_push_async_context (priv->socket_props);
548 	task = g_task_new (conn, cancellable, callback, user_data);
549 
550 	soup_socket_handshake_async (priv->socket, priv->remote_uri->host,
551 				     cancellable, start_ssl_completed, task);
552 
553 	soup_socket_properties_pop_async_context (priv->socket_props);
554 }
555 
556 gboolean
soup_connection_start_ssl_finish(SoupConnection * conn,GAsyncResult * result,GError ** error)557 soup_connection_start_ssl_finish (SoupConnection  *conn,
558 				  GAsyncResult    *result,
559 				  GError         **error)
560 {
561 	return g_task_propagate_boolean (G_TASK (result), error);
562 }
563 
564 /**
565  * soup_connection_disconnect:
566  * @conn: a connection
567  *
568  * Disconnects @conn's socket and emits a %disconnected signal.
569  * After calling this, @conn will be essentially useless.
570  **/
571 void
soup_connection_disconnect(SoupConnection * conn)572 soup_connection_disconnect (SoupConnection *conn)
573 {
574 	SoupConnectionPrivate *priv;
575 	SoupConnectionState old_state;
576 
577 	g_return_if_fail (SOUP_IS_CONNECTION (conn));
578 	priv = soup_connection_get_instance_private (conn);
579 
580 	old_state = priv->state;
581 	if (old_state != SOUP_CONNECTION_DISCONNECTED)
582 		soup_connection_set_state (conn, SOUP_CONNECTION_DISCONNECTED);
583 
584 	if (priv->socket) {
585 		SoupSocket *socket = priv->socket;
586 
587 		g_signal_handlers_disconnect_by_func (socket, G_CALLBACK (re_emit_socket_event), conn);
588 
589 		priv->socket = NULL;
590 		soup_socket_disconnect (socket);
591 		g_object_unref (socket);
592 	}
593 
594 	if (old_state != SOUP_CONNECTION_DISCONNECTED)
595 		g_signal_emit (conn, signals[DISCONNECTED], 0);
596 }
597 
598 SoupSocket *
soup_connection_get_socket(SoupConnection * conn)599 soup_connection_get_socket (SoupConnection *conn)
600 {
601 	SoupConnectionPrivate *priv = soup_connection_get_instance_private (conn);
602 
603 	g_return_val_if_fail (SOUP_IS_CONNECTION (conn), NULL);
604 
605 	return priv->socket;
606 }
607 
608 SoupURI *
soup_connection_get_remote_uri(SoupConnection * conn)609 soup_connection_get_remote_uri (SoupConnection *conn)
610 {
611 	SoupConnectionPrivate *priv = soup_connection_get_instance_private (conn);
612 
613 	g_return_val_if_fail (SOUP_IS_CONNECTION (conn), NULL);
614 
615 	return priv->remote_uri;
616 }
617 
618 SoupURI *
soup_connection_get_proxy_uri(SoupConnection * conn)619 soup_connection_get_proxy_uri (SoupConnection *conn)
620 {
621 	SoupConnectionPrivate *priv = soup_connection_get_instance_private (conn);
622 
623 	g_return_val_if_fail (SOUP_IS_CONNECTION (conn), NULL);
624 
625 	return priv->proxy_uri;
626 }
627 
628 gboolean
soup_connection_is_via_proxy(SoupConnection * conn)629 soup_connection_is_via_proxy (SoupConnection *conn)
630 {
631 	SoupConnectionPrivate *priv = soup_connection_get_instance_private (conn);
632 
633 	g_return_val_if_fail (SOUP_IS_CONNECTION (conn), FALSE);
634 
635 	return priv->proxy_uri != NULL;
636 }
637 
638 static gboolean
is_idle_connection_disconnected(SoupConnection * conn)639 is_idle_connection_disconnected (SoupConnection *conn)
640 {
641 	SoupConnectionPrivate *priv = soup_connection_get_instance_private (conn);
642 	GIOStream *iostream;
643 	GInputStream *istream;
644 	char buffer[1];
645 	GError *error = NULL;
646 
647 	if (!soup_socket_is_connected (priv->socket))
648 		return TRUE;
649 
650 	if (priv->unused_timeout && priv->unused_timeout < time (NULL))
651 		return TRUE;
652 
653 	iostream = soup_socket_get_iostream (priv->socket);
654 	istream = g_io_stream_get_input_stream (iostream);
655 
656 	/* This is tricky. The goal is to check if the socket is readable. If
657 	 * so, that means either the server has disconnected or it's broken (it
658 	 * should not send any data while the connection is in idle state). But
659 	 * we can't just check the readability of the SoupSocket because there
660 	 * could be non-application layer TLS data that is readable, but which
661 	 * we don't want to consider. So instead, just read and see if the read
662 	 * succeeds. This is OK to do here because if the read does succeed, we
663 	 * just disconnect and ignore the data anyway.
664 	 */
665 	g_pollable_input_stream_read_nonblocking (G_POLLABLE_INPUT_STREAM (istream),
666 						  &buffer, sizeof (buffer),
667 						  NULL, &error);
668 	if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) {
669 		g_clear_error (&error);
670 		return TRUE;
671 	}
672 
673 	g_error_free (error);
674 
675 	return FALSE;
676 }
677 
678 SoupConnectionState
soup_connection_get_state(SoupConnection * conn)679 soup_connection_get_state (SoupConnection *conn)
680 {
681 	SoupConnectionPrivate *priv;
682 
683 	g_return_val_if_fail (SOUP_IS_CONNECTION (conn),
684 			      SOUP_CONNECTION_DISCONNECTED);
685 	priv = soup_connection_get_instance_private (conn);
686 
687 	if (priv->state == SOUP_CONNECTION_IDLE &&
688 	    is_idle_connection_disconnected (conn))
689 		soup_connection_set_state (conn, SOUP_CONNECTION_REMOTE_DISCONNECTED);
690 
691 	return priv->state;
692 }
693 
694 void
soup_connection_set_state(SoupConnection * conn,SoupConnectionState state)695 soup_connection_set_state (SoupConnection *conn, SoupConnectionState state)
696 {
697 	SoupConnectionPrivate *priv;
698 
699 	g_return_if_fail (SOUP_IS_CONNECTION (conn));
700 	g_return_if_fail (state >= SOUP_CONNECTION_NEW &&
701 			  state <= SOUP_CONNECTION_DISCONNECTED);
702 
703 	g_object_freeze_notify (G_OBJECT (conn));
704 
705 	priv = soup_connection_get_instance_private (conn);
706 
707 	if (priv->current_msg) {
708 		g_warn_if_fail (state == SOUP_CONNECTION_IDLE ||
709 				state == SOUP_CONNECTION_DISCONNECTED);
710 		clear_current_msg (conn);
711 	}
712 
713 	if (state == SOUP_CONNECTION_IDLE && !priv->reusable) {
714 		/* This will recursively call set_state() */
715 		soup_connection_disconnect (conn);
716 	} else {
717 		priv->state = state;
718 
719 		if (priv->state == SOUP_CONNECTION_IDLE)
720 			start_idle_timer (conn);
721 
722 		g_object_notify (G_OBJECT (conn), "state");
723 	}
724 
725 	g_object_thaw_notify (G_OBJECT (conn));
726 }
727 
728 gboolean
soup_connection_get_ever_used(SoupConnection * conn)729 soup_connection_get_ever_used (SoupConnection *conn)
730 {
731 	SoupConnectionPrivate *priv = soup_connection_get_instance_private (conn);
732 
733 	g_return_val_if_fail (SOUP_IS_CONNECTION (conn), FALSE);
734 
735 	return priv->unused_timeout == 0;
736 }
737 
738 void
soup_connection_send_request(SoupConnection * conn,SoupMessageQueueItem * item,SoupMessageCompletionFn completion_cb,gpointer user_data)739 soup_connection_send_request (SoupConnection          *conn,
740 			      SoupMessageQueueItem    *item,
741 			      SoupMessageCompletionFn  completion_cb,
742 			      gpointer                 user_data)
743 {
744 	SoupConnectionPrivate *priv;
745 
746 	g_return_if_fail (SOUP_IS_CONNECTION (conn));
747 	g_return_if_fail (item != NULL);
748 	priv = soup_connection_get_instance_private (conn);
749 	g_return_if_fail (priv->state != SOUP_CONNECTION_NEW &&
750 			  priv->state != SOUP_CONNECTION_DISCONNECTED);
751 
752 	if (item->msg != priv->current_msg)
753 		set_current_msg (conn, item->msg);
754 	else
755 		priv->reusable = FALSE;
756 
757 	soup_message_send_request (item, completion_cb, user_data);
758 }
759