/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
* Copyright (C) 2007 Red Hat, Inc.
*/
#include <ctype.h>
#include <fcntl.h>
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include <glib.h>
#include <libsoup/soup-address.h>
#include <libsoup/soup-message.h>
#include <libsoup/soup-misc.h>
#include <libsoup/soup-server.h>
#include <libsoup/soup-server-message.h>
#include <libsoup/soup-session-async.h>
#include <libsoup/soup-session-sync.h>
gboolean debug = FALSE;
int errors = 0;
GThread *server_thread;
char *base_uri;
static void
dprintf (const char *format, ...)
{
va_list args;
if (!debug)
return;
va_start (args, format);
vprintf (format, args);
va_end (args);
}
static void
request_failed (SoupMessage *msg, gpointer timeout)
{
if (SOUP_STATUS_IS_TRANSPORT_ERROR (msg->status_code))
g_source_destroy (timeout);
}
static gboolean
add_body_chunk (gpointer data)
{
SoupMessage *msg = data;
soup_message_add_chunk (msg, SOUP_BUFFER_STATIC,
"OK\r\n", 4);
soup_message_add_final_chunk (msg);
soup_message_io_unpause (msg);
g_object_unref (msg);
return FALSE;
}
static void
server_callback (SoupServerContext *context, SoupMessage *msg, gpointer data)
{
GSource *timeout;
if (soup_method_get_id (msg->method) != SOUP_METHOD_ID_GET) {
soup_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED);
return;
}
if (!strcmp (context->path, "/shutdown")) {
soup_server_quit (context->server);
return;
}
soup_message_set_status (msg, SOUP_STATUS_OK);
if (!strcmp (context->path, "/fast")) {
soup_message_set_response (msg, "text/plain",
SOUP_BUFFER_STATIC, "OK\r\n", 4);
return;
}
soup_server_message_set_encoding (SOUP_SERVER_MESSAGE (msg),
SOUP_TRANSFER_CHUNKED);
g_object_ref (msg);
soup_message_io_pause (msg);
timeout = soup_add_timeout (
soup_server_get_async_context (context->server),
200, add_body_chunk, msg);
g_signal_connect (msg, "finished",
G_CALLBACK (request_failed), timeout);
}
static gpointer
run_server_thread (gpointer user_data)
{
SoupServer *server = user_data;
soup_server_add_handler (server, NULL, NULL,
server_callback, NULL, NULL);
soup_server_run (server);
g_object_unref (server);
return NULL;
}
static guint
create_server (void)
{
SoupServer *server;
GMainContext *async_context;
guint port;
async_context = g_main_context_new ();
server = soup_server_new (SOUP_SERVER_PORT, 0,
SOUP_SERVER_ASYNC_CONTEXT, async_context,
NULL);
g_main_context_unref (async_context);
if (!server) {
fprintf (stderr, "Unable to bind server\n");
exit (1);
}
port = soup_server_get_port (server);
server_thread = g_thread_create (run_server_thread, server, TRUE, NULL);
return port;
}
static void
shutdown_server (void)
{
SoupSession *session;
char *uri;
SoupMessage *msg;
session = soup_session_sync_new ();
uri = g_build_filename (base_uri, "shutdown", NULL);
msg = soup_message_new ("GET", uri);
soup_session_send_message (session, msg);
g_object_unref (msg);
g_free (uri);
soup_session_abort (session);
g_object_unref (session);
g_thread_join (server_thread);
}
/* Test 1: An async session in another thread with its own
* async_context can complete a request while the main thread's main
* loop is stopped.
*/
static gboolean idle_start_test1_thread (gpointer loop);
static gpointer test1_thread (gpointer user_data);
GCond *test1_cond;
GMutex *test1_mutex;
static void
do_test1 (void)
{
GMainLoop *loop;
dprintf ("Test 1: blocking the main thread does not block other thread\n");
test1_cond = g_cond_new ();
test1_mutex = g_mutex_new ();
loop = g_main_loop_new (NULL, FALSE);
g_idle_add (idle_start_test1_thread, loop);
g_main_loop_run (loop);
g_main_loop_unref (loop);
g_mutex_free (test1_mutex);
g_cond_free (test1_cond);
}
static gboolean
idle_start_test1_thread (gpointer loop)
{
GTimeVal time;
GThread *thread;
g_mutex_lock (test1_mutex);
thread = g_thread_create (test1_thread, base_uri, TRUE, NULL);
g_get_current_time (&time);
time.tv_sec += 5;
if (g_cond_timed_wait (test1_cond, test1_mutex, &time))
g_thread_join (thread);
else {
dprintf (" timeout!\n");
errors++;
}
g_mutex_unlock (test1_mutex);
g_main_loop_quit (loop);
return FALSE;
}
static void
test1_finished (SoupMessage *msg, gpointer loop)
{
g_main_loop_quit (loop);
}
static gpointer
test1_thread (gpointer user_data)
{
SoupSession *session;
GMainContext *async_context;
char *uri;
SoupMessage *msg;
GMainLoop *loop;
/* Wait for main thread to be waiting on test1_cond */
g_mutex_lock (test1_mutex);
g_mutex_unlock (test1_mutex);
async_context = g_main_context_new ();
session = soup_session_async_new_with_options (
SOUP_SESSION_ASYNC_CONTEXT, async_context,
NULL);
g_main_context_unref (async_context);
uri = g_build_filename (base_uri, "slow", NULL);
dprintf (" send_message\n");
msg = soup_message_new ("GET", uri);
soup_session_send_message (session, msg);
if (msg->status_code != SOUP_STATUS_OK) {
dprintf (" unexpected status: %d %s\n",
msg->status_code, msg->reason_phrase);
errors++;
}
g_object_unref (msg);
dprintf (" queue_message\n");
msg = soup_message_new ("GET", uri);
loop = g_main_loop_new (async_context, FALSE);
g_object_ref (msg);
soup_session_queue_message (session, msg, test1_finished, loop);
g_main_loop_run (loop);
g_main_loop_unref (loop);
if (msg->status_code != SOUP_STATUS_OK) {
dprintf (" unexpected status: %d %s\n",
msg->status_code, msg->reason_phrase);
errors++;
}
g_object_unref (msg);
soup_session_abort (session);
g_object_unref (session);
g_free (uri);
g_cond_signal (test1_cond);
return NULL;
}
/* Test 2: An async session in the main thread with its own
* async_context runs independently of the default main loop.
*/
static gboolean idle_test2_fail (gpointer user_data);
static void
do_test2 (void)
{
guint idle;
GMainContext *async_context;
SoupSession *session;
char *uri;
SoupMessage *msg;
dprintf ("Test 2: a session with its own context is independent of the main loop.\n");
idle = g_idle_add_full (G_PRIORITY_HIGH, idle_test2_fail, NULL, NULL);
async_context = g_main_context_new ();
session = soup_session_async_new_with_options (
SOUP_SESSION_ASYNC_CONTEXT, async_context,
NULL);
g_main_context_unref (async_context);
uri = g_build_filename (base_uri, "slow", NULL);
dprintf (" send_message\n");
msg = soup_message_new ("GET", uri);
soup_session_send_message (session, msg);
if (msg->status_code != SOUP_STATUS_OK) {
dprintf (" unexpected status: %d %s\n",
msg->status_code, msg->reason_phrase);
errors++;
}
g_object_unref (msg);
soup_session_abort (session);
g_object_unref (session);
g_free (uri);
g_source_remove (idle);
}
static gboolean
idle_test2_fail (gpointer user_data)
{
dprintf (" idle ran!\n");
errors++;
return FALSE;
}
static void
quit (int sig)
{
/* Exit cleanly on ^C in case we're valgrinding. */
exit (0);
}
int
main (int argc, char **argv)
{
int opt;
guint port;
g_type_init ();
g_thread_init (NULL);
signal (SIGINT, quit);
while ((opt = getopt (argc, argv, "d")) != -1) {
switch (opt) {
case 'd':
debug = TRUE;
break;
default:
fprintf (stderr, "Usage: %s [-d]\n",
argv[0]);
exit (1);
}
}
port = create_server ();
base_uri = g_strdup_printf ("http://localhost:%u/", port);
do_test1 ();
do_test2 ();
shutdown_server ();
g_free (base_uri);
g_main_context_unref (g_main_context_default ());
dprintf ("\n");
if (errors) {
printf ("context-test: %d error(s). Run with '-d' for details\n",
errors);
} else
printf ("context-test: OK\n");
return errors != 0;
}
syntax highlighted by Code2HTML, v. 0.9.1