libgovirt/tests/mock-httpd.c

253 lines
5.8 KiB
C

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
* Copyright (C) 2001-2003, Ximian, Inc.
*/
#include <config.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <libsoup/soup.h>
#include <glib/gstdio.h>
#include <govirt/govirt.h>
#include "mock-httpd.h"
#define GOVIRT_HTTPS_PORT 8088
struct GovirtMockHttpd {
GThread *thread;
GMainLoop *loop;
guint port;
gboolean disable_tls;
GHashTable *requests;
};
typedef struct {
char *method;
char *path;
char *content;
} GovirtMockHttpdRequest;
static const char *
govirt_mock_httpd_find_request (GovirtMockHttpd *mock_httpd,
const char *method,
const char *path)
{
GovirtMockHttpdRequest *request;
request = g_hash_table_lookup (mock_httpd->requests, path);
if (request == NULL) {
return NULL;
}
if (g_strcmp0 (request->method, method) != 0) {
return NULL;
}
return request->content;
}
static void
govirt_mock_htttpd_request_free (GovirtMockHttpdRequest *request)
{
g_free (request->method);
g_free (request->path);
g_free (request->content);
g_free (request);
}
static void
server_callback (SoupServer *server, SoupMessage *msg,
const char *path, GHashTable *query,
SoupClientContext *context, gpointer data)
{
SoupMessageHeadersIter iter;
const char *name, *value;
const char *content;
GovirtMockHttpd *mock_httpd = data;
g_debug ("%s %s HTTP/1.%d\n", msg->method, path,
soup_message_get_http_version (msg));
soup_message_headers_iter_init (&iter, msg->request_headers);
while (soup_message_headers_iter_next (&iter, &name, &value))
g_debug ("%s: %s\n", name, value);
if (msg->request_body->length)
g_debug ("%s\n", msg->request_body->data);
content = govirt_mock_httpd_find_request(mock_httpd, msg->method, path);
if (content == NULL) {
soup_message_set_status (msg, SOUP_STATUS_NOT_FOUND);
} else {
soup_message_body_append (msg->response_body, SOUP_MEMORY_STATIC,
content, strlen(content));
soup_message_set_status (msg, SOUP_STATUS_OK);
}
g_debug (" -> %d %s\n\n", msg->status_code, msg->reason_phrase);
}
static gpointer
govirt_httpd_run (gpointer user_data)
{
GovirtMockHttpd *mock_httpd = user_data;
GSList *uris, *u;
SoupServer *server;
GError *error = NULL;
GMainContext *context;
context = g_main_loop_get_context (mock_httpd->loop);
g_main_context_push_thread_default (context);
if (!mock_httpd->disable_tls) {
GTlsCertificate *cert;
cert = g_tls_certificate_new_from_files (abs_srcdir "/https-cert/server-cert.pem",
abs_srcdir "/https-cert/server-key.pem",
&error);
g_assert (error == NULL);
server = soup_server_new (SOUP_SERVER_SERVER_HEADER, "simple-soup-httpd ",
SOUP_SERVER_TLS_CERTIFICATE, cert,
NULL);
g_object_unref (cert);
soup_server_listen_local (server, mock_httpd->port,
SOUP_SERVER_LISTEN_HTTPS, &error);
} else {
server = soup_server_new (SOUP_SERVER_SERVER_HEADER, "simple-soup-httpd ",
NULL);
soup_server_listen_local (server, mock_httpd->port, 0, &error);
}
g_assert (error == NULL);
soup_server_add_handler (server, NULL,
server_callback, mock_httpd, NULL);
uris = soup_server_get_uris (server);
for (u = uris; u; u = u->next) {
char *str;
str = soup_uri_to_string (u->data, FALSE);
g_debug ("Listening on %s\n", str);
g_free (str);
soup_uri_free (u->data);
}
g_slist_free (uris);
g_debug ("\nWaiting for requests...\n");
g_main_loop_run (mock_httpd->loop);
g_main_context_pop_thread_default (context);
g_object_unref (server);
return NULL;
}
GovirtMockHttpd *
govirt_mock_httpd_new (guint port)
{
GMainContext *context;
GovirtMockHttpd *mock_httpd;
mock_httpd = g_new0 (GovirtMockHttpd, 1);
mock_httpd->port = port;
context = g_main_context_new ();
mock_httpd->loop = g_main_loop_new (context, TRUE);
g_main_context_unref (context);
mock_httpd->requests = g_hash_table_new_full (g_str_hash, g_str_equal,
NULL,
(GDestroyNotify) govirt_mock_htttpd_request_free);
return mock_httpd;
}
void
govirt_mock_httpd_disable_tls (GovirtMockHttpd *mock_httpd, gboolean disable_tls)
{
g_return_if_fail(mock_httpd->thread == NULL);
mock_httpd->disable_tls = disable_tls;
}
void
govirt_mock_httpd_add_request (GovirtMockHttpd *mock_httpd,
const char *method,
const char *path,
const char *content)
{
GovirtMockHttpdRequest *request;
g_return_if_fail(mock_httpd->thread == NULL);
/* FIXME: just one method is supported for a given path right now */
request = g_hash_table_lookup (mock_httpd->requests, path);
if (request != NULL) {
g_assert (g_strcmp0 (request->method, method) == 0);
}
request = g_new0 (GovirtMockHttpdRequest, 1);
request->method = g_strdup (method);
request->path = g_strdup (path);
request->content = g_strdup (content);
g_hash_table_replace (mock_httpd->requests, request->path, request);
}
void
govirt_mock_httpd_start (GovirtMockHttpd *mock_httpd)
{
g_return_if_fail(mock_httpd->thread == NULL);
mock_httpd->thread = g_thread_new ("simple-soup-httpd",
govirt_httpd_run, mock_httpd);
}
static gboolean
govirt_mock_httpd_stop_idle (gpointer user_data)
{
GMainLoop *loop = user_data;
if (g_main_loop_is_running (loop)) {
g_main_loop_quit (loop);
}
return G_SOURCE_REMOVE;
}
void
govirt_mock_httpd_stop (GovirtMockHttpd *mock_httpd)
{
GMainContext *context;
GSource *idle_source;
context = g_main_loop_get_context (mock_httpd->loop);
idle_source = g_idle_source_new ();
g_source_set_callback (idle_source,
govirt_mock_httpd_stop_idle,
g_main_loop_ref (mock_httpd->loop),
(GDestroyNotify)g_main_loop_unref);
g_source_attach (idle_source, context);
g_source_unref (idle_source);
g_thread_join (mock_httpd->thread);
g_hash_table_unref (mock_httpd->requests);
g_main_loop_unref (mock_httpd->loop);
g_free (mock_httpd);
}