diff options
author | Carlos Garcia Campos <cgarcia@igalia.com> | 2020-09-04 07:14:39 +0200 |
---|---|---|
committer | Patrick Griffis <tingping@tingping.se> | 2021-03-04 17:49:54 +0000 |
commit | db6bbe65fea71fc726769cd043d2e12a6cd92842 (patch) | |
tree | 5175e5b9bb3c48e01277e3ce160fd1a468f9bd7e | |
parent | b9b421d2e184107eddedd87a072f6df9c5c38e6f (diff) | |
download | libsoup-carlosgc/preconnect.tar.gz |
Add new api to preconnect to a given uricarlosgc/preconnect
A new connection is started that can be reused by a future request for
the same host. This is needed by WebKit to implement link preconnect
https://w3c.github.io/resource-hints/#preconnect
-rw-r--r-- | docs/reference/libsoup-3.0-sections.txt | 3 | ||||
-rw-r--r-- | libsoup/soup-connection.c | 11 | ||||
-rw-r--r-- | libsoup/soup-connection.h | 3 | ||||
-rw-r--r-- | libsoup/soup-session.c | 189 | ||||
-rw-r--r-- | libsoup/soup-session.h | 14 | ||||
-rw-r--r-- | tests/connection-test.c | 379 |
6 files changed, 574 insertions, 25 deletions
diff --git a/docs/reference/libsoup-3.0-sections.txt b/docs/reference/libsoup-3.0-sections.txt index 0899c3be..24a8a688 100644 --- a/docs/reference/libsoup-3.0-sections.txt +++ b/docs/reference/libsoup-3.0-sections.txt @@ -405,6 +405,9 @@ soup_session_get_features soup_session_get_feature soup_session_get_feature_for_message soup_session_has_feature +<SUBSECTION> +soup_session_preconnect_async +soup_session_preconnect_finish <SUBSECTION Standard> SOUP_IS_SESSION SOUP_IS_SESSION_CLASS diff --git a/libsoup/soup-connection.c b/libsoup/soup-connection.c index 62a48211..24a466a4 100644 --- a/libsoup/soup-connection.c +++ b/libsoup/soup-connection.c @@ -1038,6 +1038,17 @@ soup_connection_set_state (SoupConnection *conn, SoupConnectionState state) g_object_thaw_notify (G_OBJECT (conn)); } +void +soup_connection_set_reusable (SoupConnection *conn, + gboolean reusable) +{ + SoupConnectionPrivate *priv = soup_connection_get_instance_private (conn); + + g_return_if_fail (SOUP_IS_CONNECTION (conn)); + + priv->reusable = TRUE; +} + gboolean soup_connection_get_ever_used (SoupConnection *conn) { diff --git a/libsoup/soup-connection.h b/libsoup/soup-connection.h index 505866ee..0906993a 100644 --- a/libsoup/soup-connection.h +++ b/libsoup/soup-connection.h @@ -63,6 +63,9 @@ SoupConnectionState soup_connection_get_state (SoupConnection *conn); void soup_connection_set_state (SoupConnection *conn, SoupConnectionState state); +void soup_connection_set_reusable (SoupConnection *conn, + gboolean reusable); + gboolean soup_connection_get_ever_used (SoupConnection *conn); void soup_connection_send_request (SoupConnection *conn, diff --git a/libsoup/soup-session.c b/libsoup/soup-session.c index 56ce29e0..c2a2a3cb 100644 --- a/libsoup/soup-session.c +++ b/libsoup/soup-session.c @@ -1061,6 +1061,18 @@ free_host (SoupSessionHost *host) g_slice_free (SoupSessionHost, host); } +static SoupMessageQueueItem * +soup_session_lookup_queue (SoupSession *session, + gpointer data, + GCompareFunc compare_func) +{ + SoupSessionPrivate *priv = soup_session_get_instance_private (session); + GList *link; + + link = g_queue_find_custom (priv->queue, data, compare_func); + return link ? (SoupMessageQueueItem *)link->data : NULL; +} + static int lookup_message (SoupMessageQueueItem *item, SoupMessage *msg) @@ -1072,11 +1084,21 @@ static SoupMessageQueueItem * soup_session_lookup_queue_item (SoupSession *session, SoupMessage *msg) { - SoupSessionPrivate *priv = soup_session_get_instance_private (session); - GList *link; + return soup_session_lookup_queue (session, msg, (GCompareFunc)lookup_message); +} - link = g_queue_find_custom (priv->queue, msg, (GCompareFunc)lookup_message); - return link ? (SoupMessageQueueItem *)link->data : NULL; +static int +lookup_connection (SoupMessageQueueItem *item, + SoupConnection *conn) +{ + return item->conn == conn ? 0 : 1; +} + +static SoupMessageQueueItem * +soup_session_lookup_queue_item_by_connection (SoupSession *session, + SoupConnection *conn) +{ + return soup_session_lookup_queue (session, conn, (GCompareFunc)lookup_connection); } #define SOUP_SESSION_WOULD_REDIRECT_AS_GET(session, msg) \ @@ -1694,6 +1716,17 @@ connect_async_complete (GObject *object, GError *error = NULL; soup_connection_connect_finish (conn, result, &error); + if (item->related) { + SoupMessageQueueItem *new_item = item->related; + + /* Complete the preconnect successfully, since it was stolen. */ + item->state = SOUP_MESSAGE_FINISHING; + item->related = NULL; + soup_session_process_queue_item (item->session, item, NULL, FALSE); + soup_message_queue_item_unref (item); + + item = new_item; + } connect_complete (item, conn, error); if (item->state == SOUP_MESSAGE_CONNECTED || @@ -1705,6 +1738,30 @@ connect_async_complete (GObject *object, soup_message_queue_item_unref (item); } +static gboolean +steal_preconnection (SoupSession *session, + SoupMessageQueueItem *item, + SoupConnection *conn) +{ + SoupMessageQueueItem *preconnect_item; + + if (!item->async) + return FALSE; + + preconnect_item = soup_session_lookup_queue_item_by_connection (session, conn); + if (!preconnect_item) + return FALSE; + + if (!preconnect_item->connect_only || preconnect_item->state != SOUP_MESSAGE_CONNECTING) + return FALSE; + + soup_session_set_item_connection (session, preconnect_item, NULL); + g_assert (preconnect_item->related == NULL); + preconnect_item->related = soup_message_queue_item_ref (item); + + return TRUE; +} + static SoupConnection * get_connection_for_host (SoupSession *session, SoupMessageQueueItem *item, @@ -1728,11 +1785,22 @@ get_connection_for_host (SoupSession *session, for (conns = host->connections; conns; conns = conns->next) { conn = conns->data; - if (!need_new_connection && soup_connection_get_state (conn) == SOUP_CONNECTION_IDLE) { - soup_connection_set_state (conn, SOUP_CONNECTION_IN_USE); - return conn; - } else if (soup_connection_get_state (conn) == SOUP_CONNECTION_CONNECTING) + switch (soup_connection_get_state (conn)) { + case SOUP_CONNECTION_IDLE: + if (!need_new_connection) { + soup_connection_set_state (conn, SOUP_CONNECTION_IN_USE); + return conn; + } + break; + case SOUP_CONNECTION_CONNECTING: + if (steal_preconnection (session, item, conn)) + return conn; + num_pending++; + break; + default: + break; + } } /* Limit the number of pending connections; num_messages / 2 @@ -1820,9 +1888,19 @@ get_connection (SoupMessageQueueItem *item, gboolean *should_cleanup) soup_session_set_item_connection (session, item, conn); - if (soup_connection_get_state (item->conn) != SOUP_CONNECTION_NEW) { + switch (soup_connection_get_state (item->conn)) { + case SOUP_CONNECTION_IN_USE: item->state = SOUP_MESSAGE_READY; return TRUE; + case SOUP_CONNECTION_CONNECTING: + item->state = SOUP_MESSAGE_CONNECTING; + return FALSE; + case SOUP_CONNECTION_NEW: + break; + case SOUP_CONNECTION_IDLE: + case SOUP_CONNECTION_REMOTE_DISCONNECTED: + case SOUP_CONNECTION_DISCONNECTED: + g_assert_not_reached (); } item->state = SOUP_MESSAGE_CONNECTING; @@ -3707,3 +3785,96 @@ soup_session_get_original_message_for_authentication (SoupSession *session, return item->related ? item->related->msg : msg; } + +static void +preconnect_async_message_finished (SoupMessage *msg, + GTask *task) +{ + SoupMessageQueueItem *item = g_task_get_task_data (task); + + if (item->conn && !item->error) + soup_connection_set_reusable (item->conn, TRUE); +} + +static void +preconnect_async_complete (SoupSession *session, + SoupMessage *msg, + GTask *task) +{ + SoupMessageQueueItem *item = g_task_get_task_data (task); + + if (item->error) + g_task_return_error (task, g_error_copy (item->error)); + else + g_task_return_boolean (task, TRUE); + g_object_unref (task); +} + +/** + * soup_session_preconnect_async: + * @session: a #SoupSession + * @msg: a #SoupMessage + * @io_priority: the I/O priority of the request + * @cancellable: a #GCancellable + * @callback: (allow-none) (scope async): the callback to invoke when the operation finishes + * @user_data: data for @progress_callback and @callback + * + * Start a preconnection to @msg. Once the connection is done, it will remain in idle state so that + * it can be reused by future requests. If there's already an idle connection for the given @msg + * host, the operation finishes successfully without creating a new connection. If a new request + * for the given @msg host is made while the preconnect is still ongoing, the request will take + * the ownership of the connection and the preconnect operation will finish successfully (if + * there's a connection error it will be handled by the request). + * + * The operation finishes when the connection is done or an error ocurred. + */ +void +soup_session_preconnect_async (SoupSession *session, + SoupMessage *msg, + int io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + SoupMessageQueueItem *item; + GTask *task; + + g_return_if_fail (SOUP_IS_SESSION (session)); + g_return_if_fail (SOUP_IS_MESSAGE (msg)); + + task = g_task_new (session, cancellable, callback, user_data); + item = soup_session_append_queue_item (session, msg, TRUE, cancellable, + (SoupSessionCallback)preconnect_async_complete, + task); + item->connect_only = TRUE; + item->io_priority = io_priority; + g_task_set_priority (task, io_priority); + g_task_set_task_data (task, item, (GDestroyNotify)soup_message_queue_item_unref); + + g_signal_connect_object (msg, "finished", + G_CALLBACK (preconnect_async_message_finished), + task, 0); + + soup_session_kick_queue (session); +} + +/** + * soup_session_preconnect_finish: + * @session: a #SoupSession + * @result: the #GAsyncResult passed to your callback + * @error: return location for a #GError, or %NULL + * + * Complete a preconnect async operation started with soup_session_preconnect_async(). + * + * Return value: %TRUE if the preconnect succeeded, or %FALSE in case of error. + */ +gboolean +soup_session_preconnect_finish (SoupSession *session, + GAsyncResult *result, + GError **error) +{ + g_return_val_if_fail (SOUP_IS_SESSION (session), FALSE); + g_return_val_if_fail (g_task_is_valid (result, session), FALSE); + + return g_task_propagate_boolean (G_TASK (result), error); +} diff --git a/libsoup/soup-session.h b/libsoup/soup-session.h index 85a5a420..8266c9e7 100644 --- a/libsoup/soup-session.h +++ b/libsoup/soup-session.h @@ -183,4 +183,18 @@ SoupWebsocketConnection *soup_session_websocket_connect_finish (SoupSession GAsyncResult *result, GError **error); + +SOUP_AVAILABLE_IN_ALL +void soup_session_preconnect_async (SoupSession *session, + SoupMessage *msg, + int io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +SOUP_AVAILABLE_IN_ALL +gboolean soup_session_preconnect_finish (SoupSession *session, + GAsyncResult *result, + GError **error); + + G_END_DECLS diff --git a/tests/connection-test.c b/tests/connection-test.c index d4ddb8e2..cdba029b 100644 --- a/tests/connection-test.c +++ b/tests/connection-test.c @@ -660,19 +660,10 @@ do_non_idempotent_connection_test (void) soup_test_session_abort_unref (session); } -#define HTTP_SERVER "http://127.0.0.1:47524" -#define HTTPS_SERVER "https://127.0.0.1:47525" -#define HTTP_PROXY "http://127.0.0.1:47526" - -static SoupConnectionState state_transitions[] = { - /* NEW -> */ SOUP_CONNECTION_CONNECTING, - /* CONNECTING -> */ SOUP_CONNECTION_IN_USE, - /* IDLE -> */ SOUP_CONNECTION_DISCONNECTED, - /* IN_USE -> */ SOUP_CONNECTION_IDLE, - - /* REMOTE_DISCONNECTED */ -1, - /* DISCONNECTED */ -1, -}; +#define HTTP_SERVER "http://127.0.0.1:47524" +#define HTTP_SERVER_BAD_PORT "http://127.0.0.1:1234" +#define HTTPS_SERVER "https://127.0.0.1:47525" +#define HTTP_PROXY "http://127.0.0.1:47526" static const char *state_names[] = { "NEW", "CONNECTING", "IDLE", "IN_USE", @@ -689,9 +680,34 @@ connection_state_changed (SoupConnection *conn, g_object_get (conn, "state", &new_state, NULL); debug_printf (2, " %s -> %s\n", state_names[*state], state_names[new_state]); - soup_test_assert (state_transitions[*state] == new_state, - "Unexpected transition: %s -> %s\n", - state_names[*state], state_names[new_state]); + switch (*state) { + case SOUP_CONNECTION_NEW: + soup_test_assert (new_state == SOUP_CONNECTION_CONNECTING, + "Unexpected transition: %s -> %s\n", + state_names[*state], state_names[new_state]); + break; + case SOUP_CONNECTION_CONNECTING: + soup_test_assert (new_state == SOUP_CONNECTION_IN_USE || new_state == SOUP_CONNECTION_DISCONNECTED, + "Unexpected transition: %s -> %s\n", + state_names[*state], state_names[new_state]); + break; + case SOUP_CONNECTION_IDLE: + soup_test_assert (new_state == SOUP_CONNECTION_IN_USE || new_state == SOUP_CONNECTION_DISCONNECTED, + "Unexpected transition: %s -> %s\n", + state_names[*state], state_names[new_state]); + break; + case SOUP_CONNECTION_IN_USE: + soup_test_assert (new_state == SOUP_CONNECTION_IDLE, + "Unexpected transition: %s -> %s\n", + state_names[*state], state_names[new_state]); + break; + case SOUP_CONNECTION_REMOTE_DISCONNECTED: + case SOUP_CONNECTION_DISCONNECTED: + soup_test_assert (FALSE, + "Unexpected transition: %s -> %s\n", + state_names[*state], state_names[new_state]); + break; + } *state = new_state; } @@ -915,6 +931,336 @@ do_connection_event_test (void) soup_test_session_abort_unref (session); } +typedef struct { + GMainLoop *loop; + GError *error; + const char *events; + SoupConnectionState state; + SoupConnection *conn; + gboolean quit_on_preconnect; +} PreconnectTestData; + +static void +preconnection_test_message_network_event (SoupMessage *msg, + GSocketClientEvent event, + GIOStream *connection, + PreconnectTestData *data) +{ + SoupConnection *conn; + + if (event == G_SOCKET_CLIENT_RESOLVING) { + /* This is connecting, so we know it comes from a NEW state. */ + data->state = SOUP_CONNECTION_NEW; + + conn = soup_message_get_connection (msg); + g_assert_nonnull (conn); + g_assert_null (data->conn); + data->conn = g_object_ref (conn); + connection_state_changed (conn, NULL, &data->state); + + g_signal_connect (conn, "notify::state", + G_CALLBACK (connection_state_changed), + &data->state); + } + + if (soup_message_get_method (msg) == SOUP_METHOD_HEAD) { + soup_test_assert (*data->events == event_abbrevs[event], + "Unexpected event: %s (expected %s)", + event_names[event], + event_name_from_abbrev (*data->events)); + data->events = data->events + 1; + } +} + +static void +preconnection_test_request_queued (SoupSession *session, + SoupMessage *msg, + gpointer data) +{ + g_signal_connect (msg, "network-event", + G_CALLBACK (preconnection_test_message_network_event), + data); +} + +static void +preconnect_finished (SoupSession *session, + GAsyncResult *result, + PreconnectTestData *data) +{ + soup_session_preconnect_finish (session, result, &data->error); + if (data->quit_on_preconnect) + g_main_loop_quit (data->loop); +} + +static void +do_idle_connection_preconnect_test (const char *uri, + const char *proxy_uri, + const char *events) +{ + SoupSession *session; + PreconnectTestData data = { NULL, NULL, events, SOUP_CONNECTION_DISCONNECTED, NULL, TRUE }; + SoupConnection *conn; + SoupMessage *msg; + GBytes *bytes; + + session = soup_test_session_new (NULL); + + if (proxy_uri) { + GProxyResolver *resolver; + + resolver = g_simple_proxy_resolver_new (proxy_uri, NULL); + soup_session_set_proxy_resolver (session, resolver); + g_object_unref (resolver); + } + + data.loop = g_main_loop_new (NULL, FALSE); + g_signal_connect (session, "request-queued", + G_CALLBACK (preconnection_test_request_queued), + &data); + + msg = soup_message_new ("HEAD", uri); + soup_session_preconnect_async (session, msg, G_PRIORITY_DEFAULT, NULL, + (GAsyncReadyCallback)preconnect_finished, + &data); + g_object_unref (msg); + g_main_loop_run (data.loop); + g_assert_no_error (data.error); + g_assert_nonnull (data.conn); + g_assert_cmpint (data.state, ==, SOUP_CONNECTION_IDLE); + + while (*data.events) { + soup_test_assert (!*data.events, + "Expected %s", + event_name_from_abbrev (*data.events)); + data.events++; + } + + conn = data.conn; + data.conn = NULL; + msg = soup_message_new ("GET", uri); + bytes = soup_test_session_send (session, msg, NULL, NULL); + soup_test_assert_message_status (msg, SOUP_STATUS_OK); + g_object_unref (msg); + g_bytes_unref (bytes); + + /* connection-created hasn't been called. */ + g_assert_null (data.conn); + g_assert_cmpint (data.state, ==, SOUP_CONNECTION_IDLE); + + /* Preconnect again does nothing because there's already an idle connection ready. */ + msg = soup_message_new ("HEAD", uri); + soup_session_preconnect_async (session, msg, G_PRIORITY_DEFAULT, NULL, + (GAsyncReadyCallback)preconnect_finished, + &data); + g_object_unref (msg); + g_main_loop_run (data.loop); + g_assert_no_error (data.error); + g_assert_null (data.conn); + g_assert_cmpint (data.state, ==, SOUP_CONNECTION_IDLE); + + soup_session_abort (session); + g_assert_cmpint (data.state, ==, SOUP_CONNECTION_DISCONNECTED); + g_object_unref (conn); + + g_main_loop_unref (data.loop); + + soup_test_session_abort_unref (session); +} + +static void +do_idle_connection_preconnect_fail_test (const char *uri, + GQuark domain, + gint code, + const char *events) +{ + SoupSession *session; + SoupMessage *msg; + PreconnectTestData data = { NULL, NULL, events, SOUP_CONNECTION_DISCONNECTED, NULL, TRUE }; + + session = soup_test_session_new (NULL); + + if (tls_available) { + GTlsDatabase *tlsdb; + + tlsdb = g_tls_backend_get_default_database (g_tls_backend_get_default ()); + soup_session_set_tls_database (session, tlsdb); + g_object_unref (tlsdb); + } + + data.loop = g_main_loop_new (NULL, FALSE); + g_signal_connect (session, "request-queued", + G_CALLBACK (preconnection_test_request_queued), + &data); + + msg = soup_message_new ("HEAD", uri); + soup_session_preconnect_async (session, msg, G_PRIORITY_DEFAULT, NULL, + (GAsyncReadyCallback)preconnect_finished, + &data); + g_object_unref (msg); + g_main_loop_run (data.loop); + g_assert_error (data.error, domain, code); + g_error_free (data.error); + g_assert_nonnull (data.conn); + g_assert_cmpint (data.state, ==, SOUP_CONNECTION_DISCONNECTED); + g_object_unref (data.conn); + + while (*data.events) { + soup_test_assert (!*data.events, + "Expected %s", + event_name_from_abbrev (*data.events)); + data.events++; + } + + g_main_loop_unref (data.loop); + + soup_test_session_abort_unref (session); +} + +static void +do_steal_connection_preconnect_test (const char *uri, + const char *proxy_uri, + const char *events) +{ + SoupSession *session; + PreconnectTestData data = { NULL, NULL, events, SOUP_CONNECTION_DISCONNECTED, NULL, FALSE }; + SoupMessage *msg; + GBytes *bytes; + + session = soup_test_session_new (NULL); + + if (proxy_uri) { + GProxyResolver *resolver; + + resolver = g_simple_proxy_resolver_new (proxy_uri, NULL); + soup_session_set_proxy_resolver (session, resolver); + g_object_unref (resolver); + + } + + g_signal_connect (session, "request-queued", + G_CALLBACK (preconnection_test_request_queued), + &data); + + msg = soup_message_new ("HEAD", uri); + soup_session_preconnect_async (session, msg, G_PRIORITY_DEFAULT, NULL, + (GAsyncReadyCallback)preconnect_finished, + &data); + g_object_unref (msg); + + msg = soup_message_new ("GET", uri); + bytes = soup_test_session_async_send (session, msg, NULL, &data.error); + g_object_unref (msg); + g_bytes_unref (bytes); + g_assert_no_error (data.error); + g_assert_nonnull (data.conn); + g_assert_cmpint (data.state, ==, SOUP_CONNECTION_IDLE); + + while (*data.events) { + soup_test_assert (!*data.events, + "Expected %s", + event_name_from_abbrev (*data.events)); + data.events++; + } + + soup_session_abort (session); + g_assert_cmpint (data.state, ==, SOUP_CONNECTION_DISCONNECTED); + g_object_unref (data.conn); + + soup_test_session_abort_unref (session); +} + +static void +do_steal_connection_preconnect_fail_test (const char *uri, + GQuark domain, + gint code, + const char *events) +{ + SoupSession *session; + PreconnectTestData data = { NULL, NULL, events, SOUP_CONNECTION_DISCONNECTED, NULL, FALSE }; + SoupMessage *msg; + GBytes *bytes; + + session = soup_test_session_new (NULL); + + if (tls_available) { + GTlsDatabase *tlsdb; + + tlsdb = g_tls_backend_get_default_database (g_tls_backend_get_default ()); + soup_session_set_tls_database (session, tlsdb); + g_object_unref (tlsdb); + } + + g_signal_connect (session, "request-queued", + G_CALLBACK (preconnection_test_request_queued), + &data); + + msg = soup_message_new ("HEAD", uri); + soup_session_preconnect_async (session, msg, G_PRIORITY_DEFAULT, NULL, + (GAsyncReadyCallback)preconnect_finished, + &data); + g_object_unref (msg); + + msg = soup_message_new ("GET", uri); + bytes = soup_test_session_async_send (session, msg, NULL, &data.error); + g_object_unref (msg); + g_bytes_unref (bytes); + g_assert_error (data.error, domain, code); + g_error_free (data.error); + g_assert_nonnull (data.conn); + g_assert_cmpint (data.state, ==, SOUP_CONNECTION_DISCONNECTED); + g_object_unref (data.conn); + + while (*data.events) { + soup_test_assert (!*data.events, + "Expected %s", + event_name_from_abbrev (*data.events)); + data.events++; + } + + soup_test_session_abort_unref (session); +} + +static void +do_connection_preconnect_test (void) +{ + SOUP_TEST_SKIP_IF_NO_APACHE; + + debug_printf (1, " http\n"); + do_idle_connection_preconnect_test (HTTP_SERVER, NULL, "rRcCx"); + do_steal_connection_preconnect_test (HTTP_SERVER, NULL, "r"); + + debug_printf (1, " http with proxy\n"); + do_idle_connection_preconnect_test (HTTP_SERVER, HTTP_PROXY, "rRcCx"); + do_steal_connection_preconnect_test (HTTP_SERVER, HTTP_PROXY, "r"); + + debug_printf (1, " wrong http (invalid port)\n"); + do_idle_connection_preconnect_fail_test (HTTP_SERVER_BAD_PORT, + G_IO_ERROR, G_IO_ERROR_CONNECTION_REFUSED, + "rRc"); + do_steal_connection_preconnect_fail_test (HTTP_SERVER_BAD_PORT, + G_IO_ERROR, G_IO_ERROR_CONNECTION_REFUSED, + "r"); + + if (tls_available) { + debug_printf (1, " https\n"); + do_idle_connection_preconnect_test (HTTPS_SERVER, NULL, "rRcCtTx"); + do_steal_connection_preconnect_test (HTTPS_SERVER, NULL, "r"); + + debug_printf (1, " https with proxy\n"); + do_idle_connection_preconnect_test (HTTPS_SERVER, HTTP_PROXY, "rRcCpPtTx"); + do_steal_connection_preconnect_test (HTTPS_SERVER, HTTP_PROXY, "r"); + + debug_printf (1, " wrong https (invalid certificate)\n"); + do_idle_connection_preconnect_fail_test (HTTPS_SERVER, + G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE, + "rRcCt"); + do_steal_connection_preconnect_fail_test (HTTPS_SERVER, + G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE, + "r"); + } else + debug_printf (1, " https -- SKIPPING\n"); +} + int main (int argc, char **argv) { @@ -936,6 +1282,7 @@ main (int argc, char **argv) g_test_add_func ("/connection/non-idempotent", do_non_idempotent_connection_test); g_test_add_func ("/connection/state", do_connection_state_test); g_test_add_func ("/connection/event", do_connection_event_test); + g_test_add_func ("/connection/preconnect", do_connection_preconnect_test); ret = g_test_run (); |