* Copyright 2007-2012 Red Hat, Inc.
*/
#include "test-utils.h"
#include "soup-connection.h"
#include "soup-server-connection.h"
#include "soup-server-message-private.h"
#include <gio/gnetworking.h>
static SoupServer *server;
static GUri *base_uri;
static GUri *base_https_uri;
static GMutex server_mutex;
static void
forget_close (SoupServerMessage *msg,
gpointer user_data)
{
soup_message_headers_remove (soup_server_message_get_response_headers (msg),
"Connection");
}
static void
close_socket (SoupServerMessage *msg,
SoupServerConnection *conn)
{
GSocket *socket;
int sockfd;
* us to leak memory, so just shutdown the socket instead.
*/
socket = soup_server_connection_get_socket (conn);
sockfd = g_socket_get_fd (socket);
#ifdef G_OS_WIN32
shutdown (sockfd, SD_SEND);
#else
shutdown (sockfd, SHUT_WR);
#endif
* can clean up after itself properly.
*/
soup_message_body_append (soup_server_message_get_response_body (msg),
SOUP_MEMORY_STATIC,
"foo", 3);
}
static gboolean
timeout_socket (GObject *pollable,
SoupServerConnection *conn)
{
soup_server_connection_disconnect (conn);
return FALSE;
}
static void
timeout_request_finished (SoupServer *server,
SoupServerMessage *msg,
gpointer user_data)
{
SoupServerConnection *conn;
GIOStream *iostream;
GInputStream *istream;
GSource *source;
g_signal_handlers_disconnect_by_func (server, timeout_request_finished, NULL);
conn = soup_server_message_get_connection (msg);
iostream = soup_server_connection_get_iostream (conn);
istream = g_io_stream_get_input_stream (iostream);
source = g_pollable_input_stream_create_source (G_POLLABLE_INPUT_STREAM (istream), NULL);
g_source_set_callback (source, (GSourceFunc)timeout_socket, conn, NULL);
g_source_attach (source, g_main_context_get_thread_default ());
g_source_unref (source);
g_mutex_unlock (&server_mutex);
}
static void
setup_timeout_persistent (SoupServer *server,
SoupServerConnection *conn)
{
* close the connection *after* the client side writes
* the request. To ensure that this happens reliably,
* regardless of thread scheduling, we:
*
* 1. Wait for the server to finish this request and
* start reading the next one (and lock server_mutex
* to interlock with the client and ensure that it
* doesn't start writing its next request until
* that point).
* 2. Block until input stream is readable, meaning the
* client has written its request.
* 3. Close the socket.
*/
g_mutex_lock (&server_mutex);
g_signal_connect (server, "request-finished",
G_CALLBACK (timeout_request_finished), NULL);
}
static void
server_callback (SoupServer *server,
SoupServerMessage *msg,
const char *path,
GHashTable *query,
gpointer data)
{
const char *method;
* need to hold it through the whole function, so it's simpler
* to just release it right away.
*/
g_mutex_lock (&server_mutex);
g_mutex_unlock (&server_mutex);
method = soup_server_message_get_method (msg);
if (method != SOUP_METHOD_GET && method != SOUP_METHOD_POST) {
soup_server_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED, NULL);
return;
}
if (g_str_has_prefix (path, "/content-length/")) {
gboolean too_long = strcmp (path, "/content-length/long") == 0;
gboolean no_close = strcmp (path, "/content-length/noclose") == 0;
SoupMessageHeaders *response_headers;
soup_server_message_set_status (msg, SOUP_STATUS_OK, NULL);
soup_server_message_set_response (msg, "text/plain",
SOUP_MEMORY_STATIC, "foobar", 6);
response_headers = soup_server_message_get_response_headers (msg);
if (too_long)
soup_message_headers_set_content_length (response_headers, 9);
soup_message_headers_append (response_headers,
"Connection", "close");
if (too_long) {
SoupServerConnection *conn;
* another chunk after the first, to fill out
* the declared Content-Length. Instead, we
* forcibly close the socket at that point.
*/
conn = soup_server_message_get_connection (msg);
g_signal_connect (msg, "wrote-chunk",
G_CALLBACK (close_socket), conn);
} else if (no_close) {
* the headers, so that when we check it after
* writing the body, we'll think we aren't
* supposed to close it.
*/
g_signal_connect (msg, "wrote-headers",
G_CALLBACK (forget_close), NULL);
}
return;
}
if (!strcmp (path, "/timeout-persistent")) {
SoupServerConnection *conn;
conn = soup_server_message_get_connection (msg);
setup_timeout_persistent (server, conn);
}
soup_server_message_set_status (msg, SOUP_STATUS_OK, NULL);
soup_server_message_set_response (msg, "text/plain",
SOUP_MEMORY_STATIC, "index", 5);
return;
}
static void
do_content_length_framing_test (void)
{
SoupSession *session;
SoupMessage *msg;
GUri *request_uri;
goffset declared_length;
GBytes *body;
g_test_bug ("611481");
session = soup_test_session_new (NULL);
debug_printf (1, " Content-Length larger than message body length\n");
request_uri = g_uri_parse_relative (base_uri, "/content-length/long", SOUP_HTTP_URI_FLAGS, NULL);
msg = soup_message_new_from_uri ("GET", request_uri);
body = soup_session_send_and_read (session, msg, NULL, NULL);
soup_test_assert_message_status (msg, SOUP_STATUS_OK);
declared_length = soup_message_headers_get_content_length (soup_message_get_response_headers (msg));
debug_printf (2, " Content-Length: %lu, body: %s\n",
(gulong)declared_length, (char *)g_bytes_get_data (body, NULL));
g_assert_cmpint (g_bytes_get_size (body), <, declared_length);
g_uri_unref (request_uri);
g_bytes_unref (body);
g_object_unref (msg);
debug_printf (1, " Server claims 'Connection: close' but doesn't\n");
request_uri = g_uri_parse_relative (base_uri, "/content-length/noclose", SOUP_HTTP_URI_FLAGS, NULL);
msg = soup_message_new_from_uri ("GET", request_uri);
body = soup_session_send_and_read (session, msg, NULL, NULL);
soup_test_assert_message_status (msg, SOUP_STATUS_OK);
declared_length = soup_message_headers_get_content_length (soup_message_get_response_headers (msg));
g_assert_cmpint (g_bytes_get_size (body), ==, declared_length);
g_uri_unref (request_uri);
g_bytes_unref (body);
g_object_unref (msg);
soup_test_session_abort_unref (session);
}
static void
message_started_socket_collector (SoupMessage *msg,
GSocket **sockets)
{
SoupConnection *conn = soup_message_get_connection (msg);
GSocket *socket = soup_connection_get_socket (conn);
int i;
debug_printf (2, " msg %p => socket %p\n", msg, socket);
for (i = 0; i < 4; i++) {
if (!sockets[i]) {
* it gets disconnected, it doesn't get freed,
* since our checks would get messed up if the
* slice allocator reused the same address for
* two consecutive sockets.
*/
sockets[i] = g_object_ref (socket);
break;
}
}
soup_test_assert (i < 4, "socket queue overflowed");
}
static void
request_queued_socket_collector (SoupSession *session,
SoupMessage *msg,
gpointer data)
{
g_signal_connect (msg, "starting",
G_CALLBACK (message_started_socket_collector),
data);
}
static void
do_timeout_test_for_base_uri (GUri *base_uri)
{
SoupSession *session;
SoupMessage *msg;
GSocket *sockets[4] = { NULL, NULL, NULL, NULL };
GUri *timeout_uri;
int i;
GBytes *body;
session = soup_test_session_new (NULL);
g_signal_connect (session, "request-queued",
G_CALLBACK (request_queued_socket_collector),
&sockets);
debug_printf (1, " First message\n");
timeout_uri = g_uri_parse_relative (base_uri, "/timeout-persistent", SOUP_HTTP_URI_FLAGS, NULL);
msg = soup_message_new_from_uri ("GET", timeout_uri);
g_uri_unref (timeout_uri);
body = soup_session_send_and_read (session, msg, NULL, NULL);
soup_test_assert_message_status (msg, SOUP_STATUS_OK);
if (sockets[1]) {
soup_test_assert (sockets[1] == NULL, "Message was retried");
sockets[1] = sockets[2] = sockets[3] = NULL;
}
g_bytes_unref (body);
g_object_unref (msg);
* and release it when it's ready for us to send the second request.
*/
g_mutex_lock (&server_mutex);
g_mutex_unlock (&server_mutex);
debug_printf (1, " Second message\n");
msg = soup_message_new_from_uri ("GET", base_uri);
body = soup_session_send_and_read (session, msg, NULL, NULL);
soup_test_assert_message_status (msg, SOUP_STATUS_OK);
soup_test_assert (sockets[1] == sockets[0],
"Message was not retried on existing connection");
soup_test_assert (sockets[2] != NULL,
"Message was not retried after disconnect");
soup_test_assert (sockets[2] != sockets[1],
"Message was retried on closed connection");
soup_test_assert (sockets[3] == NULL,
"Message was retried again");
g_bytes_unref (body);
g_object_unref (msg);
for (i = 0; sockets[i]; i++)
g_object_unref (sockets[i]);
soup_test_session_abort_unref (session);
}
static void
do_persistent_connection_timeout_test (void)
{
g_test_bug ("631525");
debug_printf (1, " HTTP/1\n");
do_timeout_test_for_base_uri (base_uri);
debug_printf (1, " HTTP/2\n");
do_timeout_test_for_base_uri (base_https_uri);
}
static void
do_persistent_connection_timeout_test_with_cancellation_for_base_uri (GUri *base_uri)
{
SoupSession *session;
SoupMessage *msg;
GSocket *sockets[4] = { NULL, NULL, NULL, NULL };
GUri *timeout_uri;
GCancellable *cancellable;
GInputStream *response;
int i;
char buf[8192];
session = soup_test_session_new (NULL);
g_signal_connect (session, "request-queued",
G_CALLBACK (request_queued_socket_collector),
&sockets);
debug_printf (1, " First message\n");
timeout_uri = g_uri_parse_relative (base_uri, "/timeout-persistent", SOUP_HTTP_URI_FLAGS, NULL);
msg = soup_message_new_from_uri ("GET", timeout_uri);
cancellable = g_cancellable_new ();
g_uri_unref (timeout_uri);
response = soup_session_send (session, msg, cancellable, NULL);
soup_test_assert_message_status (msg, SOUP_STATUS_OK);
if (sockets[1]) {
soup_test_assert (sockets[1] == NULL, "Message was retried");
sockets[1] = sockets[2] = sockets[3] = NULL;
}
g_object_unref (msg);
soup_test_assert (response, "No response received");
while (g_input_stream_read (response, buf, sizeof (buf), NULL, NULL))
debug_printf (1, "Reading response\n");
soup_test_assert (!g_cancellable_is_cancelled (cancellable),
"User-supplied cancellable was cancelled");
g_object_unref (response);
* and release it when it's ready for us to send the second request.
*/
g_mutex_lock (&server_mutex);
g_mutex_unlock (&server_mutex);
debug_printf (1, " Second message\n");
msg = soup_message_new_from_uri ("GET", base_uri);
* was not reset below */
g_signal_connect_swapped (msg, "starting",
G_CALLBACK (g_cancellable_cancel),
cancellable);
response = soup_session_send (session, msg, cancellable, NULL);
soup_test_assert (response == NULL, "Unexpected response");
soup_test_assert_message_status (msg, SOUP_STATUS_NONE);
soup_test_assert (sockets[1] == sockets[0],
"Message was not retried on existing connection");
soup_test_assert (sockets[2] != sockets[1],
"Message was retried on closed connection");
soup_test_assert (sockets[3] == NULL,
"Message was retried again");
g_object_unref (msg);
* cancelled state */
soup_test_assert (g_cancellable_is_cancelled (cancellable),
"User-supplied cancellable was reset");
for (i = 0; sockets[i]; i++)
g_object_unref (sockets[i]);
g_object_unref (cancellable);
soup_test_session_abort_unref (session);
}
static void
do_persistent_connection_timeout_test_with_cancellation (void)
{
debug_printf (1, " HTTP/1\n");
do_persistent_connection_timeout_test_with_cancellation_for_base_uri (base_uri);
debug_printf (1, " HTTP/2\n");
do_persistent_connection_timeout_test_with_cancellation_for_base_uri (base_https_uri);
}
static GMainLoop *max_conns_loop;
static int msgs_done;
static guint quit_loop_timeout;
#define MAX_CONNS 2
#define TEST_CONNS ((MAX_CONNS * 2) + 1)
static gboolean
idle_start_server (gpointer data)
{
g_mutex_unlock (&server_mutex);
return FALSE;
}
static gboolean
quit_loop (gpointer data)
{
quit_loop_timeout = 0;
g_main_loop_quit (max_conns_loop);
return FALSE;
}
static void
max_conns_message_started (SoupMessage *msg)
{
g_signal_handlers_disconnect_by_func (msg, max_conns_message_started, NULL);
if (++msgs_done >= MAX_CONNS) {
if (quit_loop_timeout)
g_source_remove (quit_loop_timeout);
quit_loop_timeout = g_timeout_add (100, quit_loop, NULL);
}
}
static void
max_conns_request_queued (SoupSession *session,
SoupMessage *msg,
gpointer data)
{
g_signal_connect (msg, "starting",
G_CALLBACK (max_conns_message_started),
data);
}
static void
max_conns_message_complete (SoupMessage *msg, gpointer user_data)
{
if (++msgs_done == TEST_CONNS)
g_main_loop_quit (max_conns_loop);
}
static void
do_max_conns_test_for_session (SoupSession *session)
{
SoupMessage *msgs[TEST_CONNS];
int i;
GCancellable *cancellable;
max_conns_loop = g_main_loop_new (NULL, TRUE);
g_mutex_lock (&server_mutex);
cancellable = g_cancellable_new ();
g_signal_connect (session, "request-queued",
G_CALLBACK (max_conns_request_queued), NULL);
msgs_done = 0;
for (i = 0; i < TEST_CONNS; i++) {
msgs[i] = soup_message_new_from_uri ("GET", base_uri);
g_signal_connect (msgs[i], "finished",
G_CALLBACK (max_conns_message_complete), NULL);
soup_session_send_async (session, msgs[i], G_PRIORITY_DEFAULT, cancellable, NULL, NULL);
}
g_main_loop_run (max_conns_loop);
g_assert_cmpint (msgs_done, ==, MAX_CONNS);
if (quit_loop_timeout)
g_source_remove (quit_loop_timeout);
quit_loop_timeout = g_timeout_add (1000, quit_loop, NULL);
for (i = 0; i < TEST_CONNS; i++)
g_signal_handlers_disconnect_by_func (msgs[i], max_conns_message_started, NULL);
msgs_done = 0;
g_idle_add (idle_start_server, NULL);
if (quit_loop_timeout)
g_source_remove (quit_loop_timeout);
quit_loop_timeout = g_timeout_add (1000, quit_loop, NULL);
g_main_loop_run (max_conns_loop);
for (i = 0; i < TEST_CONNS; i++)
soup_test_assert_message_status (msgs[i], SOUP_STATUS_OK);
if (msgs_done != TEST_CONNS) {
* session" error.
*/
g_cancellable_cancel (cancellable);
g_main_loop_run (max_conns_loop);
}
g_object_unref (cancellable);
g_main_loop_unref (max_conns_loop);
if (quit_loop_timeout) {
g_source_remove (quit_loop_timeout);
quit_loop_timeout = 0;
}
for (i = 0; i < TEST_CONNS; i++)
g_object_unref (msgs[i]);
}
static void
do_max_conns_test (void)
{
SoupSession *session;
g_test_bug ("634422");
session = soup_test_session_new ("max-conns", MAX_CONNS,
NULL);
do_max_conns_test_for_session (session);
soup_test_session_abort_unref (session);
}
static void
np_message_started (SoupMessage *msg,
GSocket **save_socket)
{
SoupConnection *conn = soup_message_get_connection (msg);
GSocket *socket = soup_connection_get_socket (conn);
*save_socket = g_object_ref (socket);
}
static void
np_request_queued (SoupSession *session,
SoupMessage *msg,
gpointer data)
{
g_signal_connect (msg, "starting",
G_CALLBACK (np_message_started),
data);
}
static void
np_request_unqueued (SoupSession *session,
SoupMessage *msg,
GSocket **socket)
{
g_assert_false (g_socket_is_connected (*socket));
}
static void
np_request_finished (SoupMessage *msg,
gpointer user_data)
{
GMainLoop *loop = user_data;
g_main_loop_quit (loop);
}
static void
do_non_persistent_test_for_session (SoupSession *session)
{
SoupMessage *msg;
GSocket *socket = NULL;
GMainLoop *loop;
loop = g_main_loop_new (NULL, FALSE);
g_signal_connect (session, "request-queued",
G_CALLBACK (np_request_queued),
&socket);
g_signal_connect (session, "request-unqueued",
G_CALLBACK (np_request_unqueued),
&socket);
msg = soup_message_new_from_uri ("GET", base_uri);
soup_message_headers_append (soup_message_get_request_headers (msg), "Connection", "close");
g_signal_connect (msg, "finished",
G_CALLBACK (np_request_finished), loop);
soup_session_send_async (session, msg, G_PRIORITY_DEFAULT, NULL, NULL, NULL);
g_main_loop_run (loop);
g_main_loop_unref (loop);
soup_test_assert_message_status (msg, SOUP_STATUS_OK);
g_object_unref (msg);
g_object_unref (socket);
}
static void
do_non_persistent_connection_test (void)
{
SoupSession *session;
g_test_bug ("578990");
session = soup_test_session_new (NULL);
do_non_persistent_test_for_session (session);
soup_test_session_abort_unref (session);
}
static void
do_non_idempotent_test_for_session (SoupSession *session)
{
SoupMessage *msg;
GSocket *sockets[4] = { NULL, NULL, NULL, NULL };
int i;
GBytes *body;
g_signal_connect (session, "request-queued",
G_CALLBACK (request_queued_socket_collector),
&sockets);
debug_printf (2, " GET\n");
msg = soup_message_new_from_uri ("GET", base_uri);
body = soup_session_send_and_read (session, msg, NULL, NULL);
soup_test_assert_message_status (msg, SOUP_STATUS_OK);
if (sockets[1]) {
soup_test_assert (sockets[1] == NULL, "Message was retried");
sockets[1] = sockets[2] = sockets[3] = NULL;
}
g_bytes_unref (body);
g_object_unref (msg);
debug_printf (2, " POST\n");
msg = soup_message_new_from_uri ("POST", base_uri);
body = soup_session_send_and_read (session, msg, NULL, NULL);
soup_test_assert_message_status (msg, SOUP_STATUS_OK);
soup_test_assert (sockets[1] != sockets[0],
"Message was sent on existing connection");
soup_test_assert (sockets[2] == NULL,
"Too many connections used");
g_bytes_unref (body);
g_object_unref (msg);
for (i = 0; sockets[i]; i++)
g_object_unref (sockets[i]);
}
static void
do_non_idempotent_connection_test (void)
{
SoupSession *session;
session = soup_test_session_new (NULL);
do_non_idempotent_test_for_session (session);
soup_test_session_abort_unref (session);
}
#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",
"REMOTE_DISCONNECTED", "DISCONNECTED"
};
static void
connection_state_changed (SoupConnection *conn,
GParamSpec *param,
SoupConnectionState *state)
{
SoupConnectionState new_state;
g_object_get (conn, "state", &new_state, NULL);
debug_printf (2, " %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_DISCONNECTED:
soup_test_assert (FALSE,
"Unexpected transition: %s -> %s\n",
state_names[*state], state_names[new_state]);
break;
}
*state = new_state;
}
static void
message_network_event (SoupMessage *msg,
GSocketClientEvent event,
GIOStream *connection,
SoupConnectionState *state)
{
SoupConnection *conn;
if (event != G_SOCKET_CLIENT_RESOLVING)
return;
*state = SOUP_CONNECTION_NEW;
conn = soup_message_get_connection (msg);
g_assert_nonnull (conn);
connection_state_changed (conn, NULL, state);
g_signal_connect (conn, "notify::state",
G_CALLBACK (connection_state_changed),
state);
}
static void
do_one_connection_state_test (SoupSession *session,
const char *uri,
SoupConnectionState *state)
{
SoupMessage *msg;
GBytes *body;
msg = soup_message_new ("GET", uri);
g_signal_connect (msg, "network-event",
G_CALLBACK (message_network_event),
state);
body = soup_test_session_async_send (session, msg, NULL, NULL);
soup_test_assert_message_status (msg, SOUP_STATUS_OK);
g_bytes_unref (body);
g_object_unref (msg);
soup_session_abort (session);
}
static void
do_connection_state_test_for_session (SoupSession *session)
{
SoupConnectionState state;
GProxyResolver *resolver;
debug_printf (1, " http\n");
do_one_connection_state_test (session, HTTP_SERVER, &state);
if (tls_available) {
debug_printf (1, " https\n");
do_one_connection_state_test (session, HTTPS_SERVER, &state);
} else
debug_printf (1, " https -- SKIPPING\n");
resolver = g_simple_proxy_resolver_new (HTTP_PROXY, NULL);
soup_session_set_proxy_resolver (session, resolver);
g_object_unref (resolver);
debug_printf (1, " http with proxy\n");
do_one_connection_state_test (session, HTTP_SERVER, &state);
if (tls_available) {
debug_printf (1, " https with proxy\n");
do_one_connection_state_test (session, HTTPS_SERVER, &state);
} else
debug_printf (1, " https with proxy -- SKIPPING\n");
}
static void
do_connection_state_test (void)
{
SoupSession *session;
SOUP_TEST_SKIP_IF_NO_APACHE;
session = soup_test_session_new (NULL);
do_connection_state_test_for_session (session);
soup_test_session_abort_unref (session);
}
static const char *event_names[] = {
"RESOLVING", "RESOLVED", "CONNECTING", "CONNECTED",
"PROXY_NEGOTIATING", "PROXY_NEGOTIATED",
"TLS_HANDSHAKING", "TLS_HANDSHAKED", "COMPLETE"
};
static const char event_abbrevs[] = {
'r', 'R', 'c', 'C', 'p', 'P', 't', 'T', 'x', '\0'
};
static const char *
event_name_from_abbrev (char abbrev)
{
int evt;
for (evt = 0; event_abbrevs[evt]; evt++) {
if (event_abbrevs[evt] == abbrev)
return event_names[evt];
}
return "???";
}
static void
network_event (SoupMessage *msg, GSocketClientEvent event,
GIOStream *connection, gpointer user_data)
{
const char **events = user_data;
debug_printf (2, " %s\n", event_names[event]);
soup_test_assert (**events == event_abbrevs[event],
"Unexpected event: %s (expected %s)",
event_names[event],
event_name_from_abbrev (**events));
if (**events == event_abbrevs[event]) {
if (event == G_SOCKET_CLIENT_RESOLVING ||
event == G_SOCKET_CLIENT_RESOLVED) {
soup_test_assert (connection == NULL,
"Unexpectedly got connection (%s) with '%s' event",
G_OBJECT_TYPE_NAME (connection),
event_names[event]);
} else if (event < G_SOCKET_CLIENT_TLS_HANDSHAKING) {
soup_test_assert (G_IS_SOCKET_CONNECTION (connection),
"Unexpectedly got %s with '%s' event",
G_OBJECT_TYPE_NAME (connection),
event_names[event]);
} else if (event == G_SOCKET_CLIENT_TLS_HANDSHAKING ||
event == G_SOCKET_CLIENT_TLS_HANDSHAKED) {
soup_test_assert (G_IS_TLS_CLIENT_CONNECTION (connection),
"Unexpectedly got %s with '%s' event",
G_OBJECT_TYPE_NAME (connection),
event_names[event]);
} else if (event == G_SOCKET_CLIENT_COMPLETE) {
if ((*events)[-1] == 'T') {
soup_test_assert (G_IS_TLS_CLIENT_CONNECTION (connection),
"Unexpectedly got %s with '%s' event",
G_OBJECT_TYPE_NAME (connection),
event_names[event]);
} else {
soup_test_assert (G_IS_SOCKET_CONNECTION (connection),
"Unexpectedly got %s with '%s' event",
G_OBJECT_TYPE_NAME (connection),
event_names[event]);
}
}
}
if (soup_message_query_flags (msg, SOUP_MESSAGE_COLLECT_METRICS)) {
SoupMessageMetrics *metrics = soup_message_get_metrics (msg);
g_assert_cmpuint (soup_message_metrics_get_fetch_start (metrics), >, 0);
switch (event) {
case G_SOCKET_CLIENT_RESOLVING:
g_assert_cmpuint (soup_message_metrics_get_dns_start (metrics), >, 0);
break;
case G_SOCKET_CLIENT_RESOLVED:
g_assert_cmpuint (soup_message_metrics_get_dns_end (metrics), >, 0);
g_assert_cmpuint (soup_message_metrics_get_dns_end (metrics), >=, soup_message_metrics_get_dns_start (metrics));
break;
case G_SOCKET_CLIENT_CONNECTING:
g_assert_cmpuint (soup_message_metrics_get_connect_start (metrics), >, 0);
g_assert_cmpuint (soup_message_metrics_get_connect_start (metrics), >=, soup_message_metrics_get_dns_end (metrics));
break;
case G_SOCKET_CLIENT_TLS_HANDSHAKING:
g_assert_cmpuint (soup_message_metrics_get_tls_start (metrics), >, 0);
g_assert_cmpuint (soup_message_metrics_get_tls_start (metrics), >=, soup_message_metrics_get_connect_start (metrics));
break;
case G_SOCKET_CLIENT_COMPLETE:
g_assert_cmpuint (soup_message_metrics_get_connect_end (metrics), >, 0);
g_assert_cmpuint (soup_message_metrics_get_connect_end (metrics), >=, soup_message_metrics_get_connect_start (metrics));
if (soup_message_metrics_get_tls_start (metrics))
g_assert_cmpuint (soup_message_metrics_get_connect_end (metrics), >=, soup_message_metrics_get_tls_start (metrics));
break;
default:
break;
}
}
*events = *events + 1;
}
static void
metrics_test_message_starting_cb (SoupMessage *msg)
{
SoupMessageMetrics *metrics = soup_message_get_metrics (msg);
g_assert_cmpuint (soup_message_metrics_get_request_start (metrics), >, 0);
g_assert_cmpuint (soup_message_metrics_get_request_start (metrics), >=, soup_message_metrics_get_fetch_start (metrics));
}
static void
metrics_test_status_changed_cb (SoupMessage *msg)
{
SoupMessageMetrics *metrics;
metrics = soup_message_get_metrics (msg);
g_assert_cmpuint (soup_message_metrics_get_response_start (metrics), >, 0);
g_assert_cmpuint (soup_message_metrics_get_response_start (metrics), >=, soup_message_metrics_get_request_start (metrics));
}
static void
metrics_test_got_body_cb (SoupMessage *msg)
{
SoupMessageMetrics *metrics = soup_message_get_metrics (msg);
g_assert_cmpuint (soup_message_metrics_get_response_end (metrics), >, 0);
g_assert_cmpuint (soup_message_metrics_get_response_end (metrics), >=, soup_message_metrics_get_response_start (metrics));
}
static void
do_one_connection_event_test (SoupSession *session,
const char *uri,
gboolean collect_metrics,
const char *events)
{
SoupMessage *msg;
GBytes *body;
SoupMessageMetrics *metrics;
GSocketAddress *remote_address;
char *ip_address;
msg = soup_message_new ("GET", uri);
if (collect_metrics) {
soup_message_add_flags (msg, SOUP_MESSAGE_COLLECT_METRICS);
g_signal_connect (msg, "starting",
G_CALLBACK (metrics_test_message_starting_cb),
NULL);
g_signal_connect (msg, "notify::status-code",
G_CALLBACK (metrics_test_status_changed_cb),
NULL);
g_signal_connect (msg, "got-body",
G_CALLBACK (metrics_test_got_body_cb),
NULL);
}
g_signal_connect (msg, "network-event",
G_CALLBACK (network_event),
&events);
g_assert_null (soup_message_get_remote_address (msg));
body = soup_session_send_and_read (session, msg, NULL, NULL);
soup_test_assert_message_status (msg, SOUP_STATUS_OK);
while (*events) {
soup_test_assert (!*events,
"Expected %s",
event_name_from_abbrev (*events));
events++;
}
metrics = soup_message_get_metrics (msg);
if (collect_metrics) {
g_assert_nonnull (metrics);
g_assert_cmpuint (soup_message_metrics_get_fetch_start (metrics), >, 0);
g_assert_cmpuint (soup_message_metrics_get_dns_start (metrics), >, 0);
g_assert_cmpuint (soup_message_metrics_get_dns_end (metrics), >, 0);
g_assert_cmpuint (soup_message_metrics_get_connect_start (metrics), >, 0);
if (g_str_equal (uri, HTTPS_SERVER))
g_assert_cmpuint (soup_message_metrics_get_tls_start (metrics), >, 0);
g_assert_cmpuint (soup_message_metrics_get_connect_end (metrics), >, 0);
g_assert_cmpuint (soup_message_metrics_get_request_start (metrics), >, 0);
g_assert_cmpuint (soup_message_metrics_get_response_start (metrics), >, 0);
g_assert_cmpuint (soup_message_metrics_get_response_end (metrics), >, 0);
} else {
g_assert_null (metrics);
}
remote_address = soup_message_get_remote_address (msg);
g_assert_true (G_IS_INET_SOCKET_ADDRESS (remote_address));
ip_address = g_inet_address_to_string (g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (remote_address)));
g_assert_cmpstr (ip_address, ==, "127.0.0.1");
g_free (ip_address);
if (G_IS_PROXY_ADDRESS (remote_address)) {
g_assert_cmpuint (g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (remote_address)), ==, 47526);
g_assert_cmpuint (g_proxy_address_get_destination_port (G_PROXY_ADDRESS (remote_address)), ==, g_uri_get_port (soup_message_get_uri (msg)));
g_assert_cmpstr (g_proxy_address_get_destination_hostname (G_PROXY_ADDRESS (remote_address)), ==, "127.0.0.1");
} else {
g_assert_cmpuint (g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (remote_address)), ==, g_uri_get_port (soup_message_get_uri (msg)));
}
g_bytes_unref (body);
g_object_unref (msg);
soup_session_abort (session);
}
static void
do_one_connection_event_fail_test (SoupSession *session,
const char *uri,
gboolean collect_metrics,
GQuark domain,
gint code,
const char *events)
{
SoupMessage *msg;
GBytes *body;
SoupMessageMetrics *metrics;
GError *error = NULL;
GTlsDatabase *previous_tlsdb = NULL;
if (tls_available) {
GTlsDatabase *tlsdb;
previous_tlsdb = g_object_ref (soup_session_get_tls_database (session));
tlsdb = g_tls_backend_get_default_database (g_tls_backend_get_default ());
soup_session_set_tls_database (session, tlsdb);
g_object_unref (tlsdb);
}
msg = soup_message_new ("GET", uri);
if (collect_metrics) {
soup_message_add_flags (msg, SOUP_MESSAGE_COLLECT_METRICS);
g_signal_connect (msg, "starting",
G_CALLBACK (metrics_test_message_starting_cb),
NULL);
g_signal_connect (msg, "notify::status-code",
G_CALLBACK (metrics_test_status_changed_cb),
NULL);
g_signal_connect (msg, "got-body",
G_CALLBACK (metrics_test_got_body_cb),
NULL);
}
g_signal_connect (msg, "network-event",
G_CALLBACK (network_event),
&events);
body = soup_session_send_and_read (session, msg, NULL, &error);
soup_test_assert_message_status (msg, SOUP_STATUS_NONE);
g_assert_error (error, domain, code);
g_error_free (error);
while (*events) {
soup_test_assert (!*events,
"Expected %s",
event_name_from_abbrev (*events));
events++;
}
metrics = soup_message_get_metrics (msg);
if (collect_metrics) {
g_assert_nonnull (metrics);
g_assert_cmpuint (soup_message_metrics_get_fetch_start (metrics), >, 0);
g_assert_cmpuint (soup_message_metrics_get_dns_start (metrics), >, 0);
g_assert_cmpuint (soup_message_metrics_get_dns_end (metrics), >, 0);
if (g_str_equal (uri, HTTPS_SERVER))
g_assert_cmpuint (soup_message_metrics_get_tls_start (metrics), >, 0);
g_assert_cmpuint (soup_message_metrics_get_connect_end (metrics), ==, 0);
g_assert_cmpuint (soup_message_metrics_get_request_start (metrics), ==, 0);
g_assert_cmpuint (soup_message_metrics_get_response_start (metrics), ==, 0);
g_assert_cmpuint (soup_message_metrics_get_response_end (metrics), >, 0);
} else {
g_assert_null (metrics);
}
if (g_str_equal (uri, HTTPS_SERVER))
g_assert_nonnull (soup_message_get_remote_address (msg));
else
g_assert_null (soup_message_get_remote_address (msg));
g_bytes_unref (body);
g_object_unref (msg);
soup_session_abort (session);
if (tls_available) {
soup_session_set_tls_database (session, previous_tlsdb);
g_object_unref (previous_tlsdb);
}
}
static void
do_connection_event_test_for_session (SoupSession *session,
gboolean collect_metrics)
{
GProxyResolver *resolver;
debug_printf (1, " http\n");
do_one_connection_event_test (session, HTTP_SERVER, collect_metrics, "rRcCx");
debug_printf (1, " wrong http (invalid port)\n");
do_one_connection_event_fail_test (session, HTTP_SERVER_BAD_PORT, collect_metrics,
G_IO_ERROR, G_IO_ERROR_CONNECTION_REFUSED,
"rRc");
if (tls_available) {
debug_printf (1, " https\n");
do_one_connection_event_test (session, HTTPS_SERVER, collect_metrics, "rRcCtTx");
debug_printf (1, " wrong https (invalid certificate)\n");
do_one_connection_event_fail_test (session, HTTPS_SERVER, collect_metrics,
G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
"rRcCt");
} else
debug_printf (1, " https -- SKIPPING\n");
resolver = g_simple_proxy_resolver_new (HTTP_PROXY, NULL);
soup_session_set_proxy_resolver (session, resolver);
g_object_unref (resolver);
debug_printf (1, " http with proxy\n");
do_one_connection_event_test (session, HTTP_SERVER, collect_metrics, "rRcCx");
if (tls_available) {
debug_printf (1, " https with proxy\n");
do_one_connection_event_test (session, HTTPS_SERVER, collect_metrics, "rRcCpPtTx");
} else
debug_printf (1, " https with proxy -- SKIPPING\n");
}
static void
do_connection_event_test (void)
{
SoupSession *session;
SOUP_TEST_SKIP_IF_NO_APACHE;
session = soup_test_session_new (NULL);
do_connection_event_test_for_session (session, FALSE);
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) {
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_session_send_and_read (session, msg, NULL, NULL);
soup_test_assert_message_status (msg, SOUP_STATUS_OK);
g_object_unref (msg);
g_bytes_unref (bytes);
g_assert_null (data.conn);
g_assert_cmpint (data.state, ==, SOUP_CONNECTION_IDLE);
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);
}
typedef struct {
GMainLoop *loop;
guint count;
} StealPreconnectTestData;
static void
steal_preconnect_finished (SoupSession *session,
GAsyncResult *result,
StealPreconnectTestData *data)
{
soup_session_preconnect_finish (session, result, NULL);
if (++data->count == 2)
g_main_loop_quit (data->loop);
}
static void
steal_preconnect_send_and_read_finished (SoupSession *session,
GAsyncResult *result,
StealPreconnectTestData *data)
{
GBytes *bytes;
bytes = soup_session_send_and_read_finish (session, result, NULL);
g_bytes_unref (bytes);
if (++data->count == 2)
g_main_loop_quit (data->loop);
}
static void
do_steal_preconnect_connection_test (void)
{
SoupSession *session;
SoupMessage *msg;
StealPreconnectTestData data = { NULL, 0 };
PreconnectTestData data1 = { NULL, NULL, "rRcCx", SOUP_CONNECTION_DISCONNECTED, NULL, FALSE };
PreconnectTestData data2 = { NULL, NULL, "rRcCx", SOUP_CONNECTION_DISCONNECTED, NULL, FALSE };
session = soup_test_session_new (NULL);
data.loop = g_main_loop_new (NULL, FALSE);
msg = soup_message_new ("HEAD", HTTP_SERVER);
g_signal_connect (msg, "network-event",
G_CALLBACK (preconnection_test_message_network_event),
&data1);
soup_session_preconnect_async (session, msg, G_PRIORITY_DEFAULT, NULL,
(GAsyncReadyCallback)steal_preconnect_finished,
&data);
g_object_unref (msg);
msg = soup_message_new ("HEAD", HTTP_SERVER);
g_signal_connect (msg, "network-event",
G_CALLBACK (preconnection_test_message_network_event),
&data2);
soup_session_preconnect_async (session, msg, G_PRIORITY_DEFAULT, NULL,
(GAsyncReadyCallback)steal_preconnect_finished,
&data);
g_object_unref (msg);
g_main_loop_run (data.loop);
g_assert_nonnull (data1.conn);
g_assert_nonnull (data2.conn);
g_assert_false (data1.conn == data2.conn);
g_assert_cmpint (data1.state, ==, SOUP_CONNECTION_IDLE);
g_assert_cmpint (data2.state, ==, SOUP_CONNECTION_IDLE);
while (*data1.events) {
soup_test_assert (!*data1.events,
"Expected %s",
event_name_from_abbrev (*data1.events));
soup_test_assert (!*data2.events,
"Expected %s",
event_name_from_abbrev (*data2.events));
data1.events++;
data2.events++;
}
data.count = 0;
g_clear_object (&data1.conn);
g_clear_object (&data2.conn);
msg = soup_message_new ("GET", HTTP_SERVER);
g_signal_connect (msg, "network-event",
G_CALLBACK (preconnection_test_message_network_event),
&data1);
soup_session_send_and_read_async (session, msg, G_PRIORITY_DEFAULT, NULL,
(GAsyncReadyCallback)steal_preconnect_send_and_read_finished,
&data);
g_object_unref (msg);
msg = soup_message_new ("GET", HTTP_SERVER);
g_signal_connect (msg, "network-event",
G_CALLBACK (preconnection_test_message_network_event),
&data2);
soup_session_send_and_read_async (session, msg, G_PRIORITY_DEFAULT, NULL,
(GAsyncReadyCallback)steal_preconnect_send_and_read_finished,
&data);
g_object_unref (msg);
g_main_loop_run (data.loop);
g_assert_null (data1.conn);
g_assert_null (data2.conn);
g_assert_cmpint (data1.state, ==, SOUP_CONNECTION_IDLE);
g_assert_cmpint (data2.state, ==, SOUP_CONNECTION_IDLE);
g_main_loop_unref (data.loop);
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");
debug_printf (1, " preconnect should never steal a connection\n");
do_steal_preconnect_connection_test ();
}
static void
do_connection_metrics_test (void)
{
SoupSession *session;
SOUP_TEST_SKIP_IF_NO_APACHE;
session = soup_test_session_new (NULL);
do_connection_event_test_for_session (session, TRUE);
soup_test_session_abort_unref (session);
}
static void
force_http2_test_network_event (SoupMessage *msg,
GSocketClientEvent event,
GIOStream *connection,
SoupConnection **conn)
{
if (event != G_SOCKET_CLIENT_RESOLVING)
return;
*conn = soup_message_get_connection (msg);
}
static void
do_connection_force_http2_test (void)
{
SoupSession *session;
SoupMessage *msg;
SoupConnection *conn1 = NULL;
SoupConnection *conn2 = NULL;
GBytes *body;
SOUP_TEST_SKIP_IF_NO_TLS;
SOUP_TEST_SKIP_IF_NO_APACHE;
session = soup_test_session_new (NULL);
msg = soup_message_new ("GET", HTTPS_SERVER);
g_signal_connect (msg, "network-event",
G_CALLBACK (force_http2_test_network_event),
&conn1);
body = soup_session_send_and_read (session, msg, NULL, NULL);
g_assert_nonnull (conn1);
g_assert_cmpint (soup_connection_get_state (conn1), ==, SOUP_CONNECTION_IDLE);
g_assert_cmpint (soup_connection_get_negotiated_protocol (conn1), ==, SOUP_HTTP_1_1);
g_object_unref (msg);
g_bytes_unref (body);
msg = soup_message_new ("GET", HTTPS_SERVER);
g_signal_connect (msg, "network-event",
G_CALLBACK (force_http2_test_network_event),
&conn2);
soup_message_set_force_http_version (msg, SOUP_HTTP_2_0);
body = soup_session_send_and_read (session, msg, NULL, NULL);
g_assert_nonnull (conn2);
g_assert_cmpint (soup_connection_get_state (conn2), ==, SOUP_CONNECTION_IDLE);
g_assert_cmpint (soup_connection_get_negotiated_protocol (conn2), ==, SOUP_HTTP_2_0);
g_assert_false (conn1 == conn2);
g_object_unref (msg);
g_bytes_unref (body);
soup_test_session_abort_unref (session);
}
static void
message_restarted (SoupMessage *msg,
gboolean *was_restarted)
{
*was_restarted = TRUE;
}
static void
do_connection_http_1_1_required_test (void)
{
SoupSession *session;
SoupMessage *msg;
GBytes *body;
gboolean was_restarted = FALSE;
GError *error = NULL;
SOUP_TEST_SKIP_IF_NO_TLS;
SOUP_TEST_SKIP_IF_NO_APACHE;
session = soup_test_session_new (NULL);
msg = soup_message_new ("GET", "https://127.0.0.1:47525/client-cert");
soup_message_set_force_http_version (msg, SOUP_HTTP_2_0);
g_signal_connect (msg, "restarted",
G_CALLBACK (message_restarted), &was_restarted);
body = soup_test_session_async_send (session, msg, NULL, &error);
g_assert_no_error (error);
g_assert_cmpuint (soup_message_get_status (msg), ==, 403);
g_assert_true (was_restarted);
g_assert_nonnull (body);
g_bytes_unref (body);
g_object_unref (msg);
soup_test_session_abort_unref (session);
}
int
main (int argc, char **argv)
{
int ret;
test_init (argc, argv, NULL);
apache_init ();
server = soup_test_server_new (SOUP_TEST_SERVER_IN_THREAD | SOUP_TEST_SERVER_HTTP2);
soup_server_add_handler (server, NULL, server_callback, NULL, NULL);
base_uri = soup_test_server_get_uri (server, "http", NULL);
base_https_uri = soup_test_server_get_uri (server, "https", NULL);
g_test_add_func ("/connection/content-length-framing", do_content_length_framing_test);
g_test_add_func ("/connection/persistent-connection-timeout", do_persistent_connection_timeout_test);
g_test_add_func ("/connection/persistent-connection-timeout-with-cancellable",
do_persistent_connection_timeout_test_with_cancellation);
g_test_add_func ("/connection/max-conns", do_max_conns_test);
g_test_add_func ("/connection/non-persistent", do_non_persistent_connection_test);
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);
g_test_add_func ("/connection/metrics", do_connection_metrics_test);
g_test_add_func ("/connection/force-http2", do_connection_force_http2_test);
g_test_add_func ("/connection/http2/http-1-1-required", do_connection_http_1_1_required_test);
ret = g_test_run ();
g_uri_unref (base_uri);
soup_test_server_quit_unref (server);
test_cleanup ();
return ret;
}