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