/*
 * libsyncml - A syncml protocol implementation
 * Copyright (C) 2005  Armin Bauer <armin.bauer@opensync.org>
 * Copyright (C) 2008  Michael Bell <michael.bell@opensync.org>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
 *
 */
 
#include "tests/support.h"

#include <libsyncml/sml_transport_internals.h>

int client_connects;
int client_disconnects;
int client_receives;
int client_errors;

int server_connects;
int server_disconnects;
int server_receives;

GList *links;

void reset_testbed()
{
	client_connects = 0;
	client_disconnects = 0;
	client_receives = 0;
	client_errors = 0;

	server_connects = 0;
	server_disconnects = 0;
	server_receives = 0;

	links = NULL;

	setup_testbed(NULL);
}

START_TEST (http_client_new)
{
	reset_testbed();
	
	SmlError *error = NULL;
	SmlTransport *tsp = smlTransportNew(SML_TRANSPORT_HTTP_CLIENT, &error);
	sml_fail_unless(tsp != NULL, NULL);
	sml_fail_unless(error == NULL, NULL);
	
	smlTransportFree(tsp);
}
END_TEST

START_TEST (http_client_init)
{
	reset_testbed();
	
	SmlError *error = NULL;
	SmlTransport *tsp = smlTransportNew(SML_TRANSPORT_HTTP_CLIENT, &error);
	sml_fail_unless(tsp != NULL, NULL);
	sml_fail_unless(error == NULL, NULL);
	
	sml_fail_unless(smlTransportSetConfigOption(tsp, "URL", "http://libsyncml.opensyncml.org", &error), "%s", smlErrorPrint(&error));

	sml_fail_unless(smlTransportInitialize(tsp, &error), "%s", smlErrorPrint(&error));
	sml_fail_unless(error == NULL, "%s", smlErrorPrint(&error));
	
	sml_fail_unless(smlTransportFinalize(tsp, &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	
	smlTransportFree(tsp);
}
END_TEST

/* TODO: Review if it's possible to fool libsoup with a wrong port.
START_TEST (http_client_wrong_port)
{
	reset_testbed();
	
	SmlError *error = NULL;
	SmlTransport *tsp = smlTransportNew(SML_TRANSPORT_HTTP_CLIENT, &error);
	sml_fail_unless(tsp != NULL, NULL);
	sml_fail_unless(error == NULL, NULL);
	
	SmlTransportHttpClientConfig config;
	memset(&config, 0, sizeof(SmlTransportHttpClientConfig));
	config.url = "http://libsyncml.opensync.org:-1/";
	
	sml_fail_unless(!smlTransportInitialize(tsp, &config, &error), NULL);
	sml_fail_unless(error != NULL, NULL);
	
	smlErrorDeref(&error);
	
	smlTransportFree(tsp);
}
END_TEST
*/

START_TEST (http_client_init_no_url)
{
	/* An HTTP client without an URL is an ERROR. */

	reset_testbed();
	
	SmlError *error = NULL;
	SmlTransport *tsp = smlTransportNew(SML_TRANSPORT_HTTP_CLIENT, &error);
	sml_fail_unless(tsp != NULL, NULL);
	sml_fail_unless(error == NULL, NULL);

	/* The port alone is a valid URL. */
	sml_fail_unless(smlTransportSetConfigOption(tsp, "URL", "http://:80", &error), NULL);
	sml_fail_unless(error == NULL, NULL);

	/* The transport is not configured but libsoup can initialize a default client */
	sml_fail_unless(smlTransportInitialize(tsp, &error), "%s", smlErrorPrint(&error));
	sml_fail_unless(error == NULL, "%s", smlErrorPrint(&error));

	sml_fail_unless(smlTransportFinalize(tsp, &error), "%s", smlErrorPrint(&error));
	sml_fail_unless(error == NULL, "%s", smlErrorPrint(&error));

	smlTransportFree(tsp);
}
END_TEST

START_TEST (http_server_new)
{
	reset_testbed();
	
	SmlError *error = NULL;
	SmlTransport *tsp = smlTransportNew(SML_TRANSPORT_HTTP_SERVER, &error);
	sml_fail_unless(tsp != NULL, NULL);
	sml_fail_unless(error == NULL, NULL);
	
	smlTransportFree(tsp);
}
END_TEST

SmlBool _recv_client_event(SmlTransport *tsp, SmlLink *link_, SmlTransportEventType type, SmlTransportData *data, SmlError *error, void *userdata)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p, %d)", __func__, tsp, link_, type);
	
	sml_fail_unless(GPOINTER_TO_INT(userdata) == 1, NULL);
	
	switch (type) {
		case SML_TRANSPORT_EVENT_CONNECT_DONE:
			g_atomic_int_inc(&client_connects);
			break;
		case SML_TRANSPORT_EVENT_DISCONNECT_DONE:
			g_atomic_int_inc(&client_disconnects);
			break;
		case SML_TRANSPORT_EVENT_DATA:
			sml_fail_unless(!strcmp(data->data, "answer"), NULL);
			sml_fail_unless(data->size == 7, NULL);
			sml_fail_unless(data->type == SML_MIMETYPE_WBXML, NULL);
			g_atomic_int_inc(&client_receives);
			break;
		case SML_TRANSPORT_EVENT_ERROR:
			sml_fail_unless(error != NULL, NULL);
			g_atomic_int_inc(&client_errors);
			break;
		default:
			sml_fail_unless(FALSE, "An unexpected transport event %d was received.", type);
			break;
	}
	
	smlTrace(TRACE_EXIT, "%s()", __func__);
	return TRUE;
}

SmlBool _recv_server_event(SmlTransport *tsp, SmlLink *link_, SmlTransportEventType type, SmlTransportData *data, SmlError *error, void *userdata)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %d, %p)", __func__, link_, type, error);
	SmlError *locerror = NULL;
	SmlLink *last = NULL;
		
	switch (type) {
                case SML_TRANSPORT_EVENT_CONNECT_DONE:
			g_atomic_int_inc(&server_connects);
			links = g_list_append(links, link_);
			smlLinkRef(link_);
                        break;
                case SML_TRANSPORT_EVENT_DISCONNECT_DONE:
			/* only real connections must be managed */
			last = g_list_nth_data(links, g_list_length(links)-1);
			sml_fail_unless(last == link_, "The disconnected links do not match.");
			links = g_list_remove(links, last);
			smlLinkDeref(last);
			g_atomic_int_inc(&server_disconnects);
                        break;
		case SML_TRANSPORT_EVENT_DATA:
			g_atomic_int_inc(&server_receives);
			
			if (!strcmp(data->data, "test")) {
				sml_fail_unless(data->size == 5, NULL);
				sml_fail_unless(data->type == SML_MIMETYPE_XML, NULL);
				sml_fail_unless(link_ != NULL, NULL);
				
				if (GPOINTER_TO_INT(userdata) == 1) {
					data = smlTransportDataNew((char *)"answer", 7, SML_MIMETYPE_WBXML, FALSE, &locerror);
					sml_fail_unless(data != NULL, NULL);
					sml_fail_unless(locerror == NULL, "%s", smlErrorPrint(&locerror));
				
					sml_fail_unless(smlTransportSend(tsp, link_, data, &locerror), "%s", smlErrorPrint(&locerror));
					
					smlTransportDataDeref(data);
				} else if (GPOINTER_TO_INT(userdata) == 2) {
					SmlError *newerror = NULL;
					smlErrorSet(&newerror, SML_ERROR_GENERIC, "test");
					smlTransportSetError(tsp, link_, &newerror);
					smlErrorDeref(&newerror);
				} else if (GPOINTER_TO_INT(userdata) == 3) {
					sml_fail_unless(smlTransportDisconnect(tsp, link_, &locerror), "%s", smlErrorPrint(&locerror));
				} else {
					sml_fail_unless(FALSE, "Some unexpected userdata %i was detected.", GPOINTER_TO_INT(userdata));
				}
			} else if (!strcmp(data->data, "error")) {
				sml_fail_unless(data->size == 6, NULL);
				sml_fail_unless(data->type == SML_MIMETYPE_XML, NULL);
				sml_fail_unless(link_ != NULL, NULL);
				
				SmlError *newerror = NULL;
				smlErrorSet(&newerror, SML_ERROR_GENERIC, "test2");
				smlTransportSetError(tsp, link_, &newerror);
				smlErrorDeref(&newerror);
			} else {
				sml_fail_unless(FALSE, "The received data was not expected (%s).", data->data);
			}
			break;
		default:
			sml_fail_unless(FALSE, "An unexpected transport event %d was received.", type);
			break;
	}
	
	smlTrace(TRACE_EXIT, "%s()", __func__);
	return TRUE;
}

START_TEST (http_server_init)
{
	reset_testbed();
	
	SmlError *error = NULL;
	SmlTransport *tsp = smlTransportNew(SML_TRANSPORT_HTTP_SERVER, &error);
	sml_fail_unless(tsp != NULL, NULL);
	sml_fail_unless(error == NULL, NULL);
	
	sml_fail_unless(smlTransportSetConfigOption(tsp, "PORT", "32425", &error), NULL);
	sml_fail_unless(smlTransportSetConfigOption(tsp, "URL", "/test", &error), NULL);
	
	smlTransportSetEventCallback(tsp, _recv_server_event, GINT_TO_POINTER(1));

	sml_fail_unless(smlTransportInitialize(tsp, &error), "%s", smlErrorPrint(&error));
	sml_fail_unless(error == NULL, "%s", smlErrorPrint(&error));
	
	sml_fail_unless(smlTransportFinalize(tsp, &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	
	smlTransportFree(tsp);
}
END_TEST

START_TEST (http_server_wrong_port)
{
	reset_testbed();
	
	SmlError *error = NULL;
	SmlTransport *tsp = smlTransportNew(SML_TRANSPORT_HTTP_SERVER, &error);
	sml_fail_unless(tsp != NULL, NULL);
	sml_fail_unless(error == NULL, NULL);

	/* -1 is an illegal port. */
	sml_fail_unless(smlTransportSetConfigOption(tsp, "PORT", "-1", &error) == FALSE, NULL);
	sml_fail_unless(error != NULL, NULL);
	smlErrorDeref(&error);

	/* The default config of libsoup can be used to setup a valid transport. */
	sml_fail_unless(smlTransportInitialize(tsp, &error), "%s", smlErrorPrint(&error));
	sml_fail_unless(error == NULL, "%s", smlErrorPrint(&error));
	
	sml_fail_unless(smlTransportFinalize(tsp, &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	
	smlTransportFree(tsp);
}
END_TEST

START_TEST (http_connect)
{
	reset_testbed();
	
	SmlError *error = NULL;
	SmlTransport *server = smlTransportNew(SML_TRANSPORT_HTTP_SERVER, &error);
	SmlTransport *client = smlTransportNew(SML_TRANSPORT_HTTP_CLIENT, &error);

	sml_fail_unless(smlTransportSetConfigOption(client, "URL", "http://127.0.0.1:10008", &error), NULL);

	sml_fail_unless(smlTransportSetConfigOption(server, "PORT", "10008", &error), NULL);

	smlTransportSetEventCallback(client, _recv_client_event, GINT_TO_POINTER(1));
	smlTransportSetEventCallback(server, _recv_server_event, GINT_TO_POINTER(1));

	sml_fail_unless(smlTransportInitialize(client, &error), "%s", smlErrorPrint(&error));
	sml_fail_unless(smlTransportInitialize(server, &error), "%s", smlErrorPrint(&error));
	
	sml_fail_unless(smlTransportConnect(client, &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	
	while (client_connects == 0) { usleep(50); };
	
	sml_fail_unless(client_connects == 1, NULL);
	sml_fail_unless(client_disconnects == 0, NULL);
	
	/* disconnect the client */

	sml_fail_unless(smlTransportDisconnect(client, NULL, &error), "%s", smlErrorPrint(&error));
	sml_fail_unless(error == NULL, NULL);
	
	while (client_disconnects == 0) { usleep(50); };
	
	sml_fail_unless(client_connects == 1, NULL);
	sml_fail_unless(client_disconnects == 1, NULL);

	/* cleanup the transports */

	sml_fail_unless(smlTransportFinalize(server, &error), NULL);
	sml_fail_unless(smlTransportFinalize(client, &error), NULL);
	
	smlTransportFree(server);
	smlTransportFree(client);
}
END_TEST

START_TEST (http_send)
{
	reset_testbed();
	
	SmlError *error = NULL;
	SmlTransport *server = smlTransportNew(SML_TRANSPORT_HTTP_SERVER, &error);
	SmlTransport *client = smlTransportNew(SML_TRANSPORT_HTTP_CLIENT, &error);
	
	sml_fail_unless(smlTransportSetConfigOption(client, "URL", "http://127.0.0.1:10009", &error), NULL);

	sml_fail_unless(smlTransportSetConfigOption(server, "PORT", "10009", &error), NULL);

	smlTransportSetEventCallback(server, _recv_server_event, GINT_TO_POINTER(1));
	smlTransportSetEventCallback(client, _recv_client_event, GINT_TO_POINTER(1));
	
	sml_fail_unless(smlTransportInitialize(client, &error), "%s", smlErrorPrint(&error));
	sml_fail_unless(smlTransportInitialize(server, &error), "%s", smlErrorPrint(&error));

	SmlTransportData *data = smlTransportDataNew((char *)"test", 5, SML_MIMETYPE_XML, FALSE, &error);
	sml_fail_unless(data != NULL, NULL);
	sml_fail_unless(error == NULL, NULL);

	sml_fail_unless(smlTransportSend(client, NULL, data, &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	
	smlTransportDataDeref(data);
	while(client_connects < 1) { usleep(50); }

	/* disconnect client */

	sml_fail_unless(smlTransportDisconnect(client, NULL, &error), "%s", smlErrorPrint(&error));
	while(client_disconnects < 1) { usleep(50); }

	/* disconnect server */

	sml_fail_unless(smlTransportDisconnect(server, g_list_nth_data(links, 0), &error), NULL);
	while(server_disconnects < 1) { usleep(50); }

	/* cleanup transports */
	
	sml_fail_unless(smlTransportFinalize(server, &error), NULL);
	sml_fail_unless(smlTransportFinalize(client, &error), NULL);
	
	smlTransportFree(server);
	smlTransportFree(client);
}
END_TEST

START_TEST (http_receive)
{
	reset_testbed();
	
	SmlError *error = NULL;
	SmlTransport *server = smlTransportNew(SML_TRANSPORT_HTTP_SERVER, &error);
	SmlTransport *client = smlTransportNew(SML_TRANSPORT_HTTP_CLIENT, &error);
	

	sml_fail_unless(smlTransportSetConfigOption(client, "URL", "http://127.0.0.1:10010", &error), NULL);

	sml_fail_unless(smlTransportSetConfigOption(server, "PORT", "10010", &error), NULL);

	smlTransportSetEventCallback(server, _recv_server_event, GINT_TO_POINTER(3));
	smlTransportSetEventCallback(client, _recv_client_event, GINT_TO_POINTER(1));

	sml_fail_unless(smlTransportInitialize(client, &error), "%s", smlErrorPrint(&error));
	sml_fail_unless(smlTransportInitialize(server, &error), "%s", smlErrorPrint(&error));

	SmlTransportData *data = smlTransportDataNew((char *)"test", 5, SML_MIMETYPE_XML, FALSE, &error);
	sml_fail_unless(data != NULL, NULL);
	sml_fail_unless(error == NULL, NULL);

	sml_fail_unless(smlTransportSend(client, NULL, data, &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	
	smlTransportDataDeref(data);
	
	while (server_receives < 1) {
		usleep(100);
	}
	
	sml_fail_unless(server_connects == 1, NULL);
	sml_fail_unless(server_receives == 1, NULL);

	/* the client must not be disconnected
	 * because the server does not accept the connection
	 */

	while(server_disconnects < 1) { usleep(50); }

	/* cleanup transports */
	
	sml_fail_unless(smlTransportFinalize(server, &error), NULL);
	sml_fail_unless(smlTransportFinalize(client, &error), NULL);
	
	smlTransportFree(server);
	smlTransportFree(client);
}
END_TEST

START_TEST (http_reply)
{
	reset_testbed();
	
	SmlError *error = NULL;
	SmlTransport *server = smlTransportNew(SML_TRANSPORT_HTTP_SERVER, &error);
	SmlTransport *client = smlTransportNew(SML_TRANSPORT_HTTP_CLIENT, &error);
	
	sml_fail_unless(smlTransportSetConfigOption(client, "URL", "http://127.0.0.1:10011", &error), NULL);

	sml_fail_unless(smlTransportSetConfigOption(server, "PORT", "10011", &error), NULL);

	smlTransportSetEventCallback(client, _recv_client_event, GINT_TO_POINTER(1));
	smlTransportSetEventCallback(server, _recv_server_event, GINT_TO_POINTER(1));

	sml_fail_unless(smlTransportInitialize(client, &error), "%s", smlErrorPrint(&error));
	sml_fail_unless(smlTransportInitialize(server, &error), "%s", smlErrorPrint(&error));

	SmlTransportData *data = smlTransportDataNew((char *)"test", 5, SML_MIMETYPE_XML, FALSE, &error);
	sml_fail_unless(data != NULL, NULL);
	sml_fail_unless(error == NULL, NULL);

	sml_fail_unless(smlTransportSend(client, NULL, data, &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	
	smlTransportDataDeref(data);
	
	while (client_receives < 1) {
		usleep(100);
	}
	
	sml_fail_unless(client_receives == 1, NULL);
	sml_fail_unless(server_receives == 1, NULL);

	/* disconnect client */

	sml_fail_unless(smlTransportDisconnect(client, NULL, &error), "%s", smlErrorPrint(&error));
	while(client_disconnects < 1) { usleep(50); }

	/* disconnect server */

	sml_fail_unless(smlTransportDisconnect(server, g_list_nth_data(links, 0), &error), NULL);
	while(server_disconnects < 1) { usleep(50); }

	/* cleanup transports */
	
	sml_fail_unless(smlTransportFinalize(server, &error), NULL);
	sml_fail_unless(smlTransportFinalize(client, &error), NULL);
	
	smlTransportFree(server);
	smlTransportFree(client);
}
END_TEST

START_TEST (http_talk)
{
	reset_testbed();
	
	SmlError *error = NULL;
	SmlTransport *server = smlTransportNew(SML_TRANSPORT_HTTP_SERVER, &error);
	SmlTransport *client = smlTransportNew(SML_TRANSPORT_HTTP_CLIENT, &error);
	
	sml_fail_unless(smlTransportSetConfigOption(client, "URL", "http://127.0.0.1:10012", &error), NULL);

	sml_fail_unless(smlTransportSetConfigOption(server, "PORT", "10012", &error), NULL);

	smlTransportSetEventCallback(client, _recv_client_event, GINT_TO_POINTER(1));
	smlTransportSetEventCallback(server, _recv_server_event, GINT_TO_POINTER(1));

	sml_fail_unless(smlTransportInitialize(client, &error), "%s", smlErrorPrint(&error));
	sml_fail_unless(smlTransportInitialize(server, &error), "%s", smlErrorPrint(&error));

	SmlTransportData *data = NULL;
	
	int i;
	for (i = 0; i < 20; i++) {
		data = smlTransportDataNew((char *)"test", 5, SML_MIMETYPE_XML, FALSE, &error);
		sml_fail_unless(data != NULL, NULL);
		sml_fail_unless(error == NULL, NULL);
	
		sml_fail_unless(smlTransportSend(client, NULL, data, &error), NULL);
		sml_fail_unless(error == NULL, NULL);
		
		smlTransportDataDeref(data);
		
		while (1) {
			usleep(100);
			if (client_receives == i+1)
				break;
		}
	}
	
	sml_fail_unless(client_receives == 20, NULL);
	sml_fail_unless(server_receives == 20, NULL);
	
	/* disconnect client */

	sml_fail_unless(smlTransportDisconnect(client, NULL, &error), "%s", smlErrorPrint(&error));
	while(client_disconnects < 1) { usleep(50); }

	/* disconnect server */

	for (i = 0; i < 20; i++) {
		/* start to disconnect from the end
		 * to avoid conflicts with very fast SMP machines
		 * which can process disconnects so fast
		 * that the list will be cleaned up before
		 * this for-loop ends
		 */
		smlTrace(TRACE_INTERNAL, "%s: Perparing next disconnect", __func__);
		sml_fail_unless(smlTransportDisconnect(server, g_list_nth_data(links, 19-i), &error), NULL);
		while(server_disconnects < i+1) {
			usleep(100);
		}
	}

	/* cleanup transports */
	
	sml_fail_unless(smlTransportFinalize(server, &error), NULL);
	sml_fail_unless(smlTransportFinalize(client, &error), NULL);
	
	smlTransportFree(server);
	smlTransportFree(client);
}
END_TEST

START_TEST (http_connect_error)
{
	reset_testbed();
	
	SmlError *error = NULL;
	SmlTransport *client = smlTransportNew(SML_TRANSPORT_HTTP_CLIENT, &error);
	
	sml_fail_unless(smlTransportSetConfigOption(client, "URL", "http://127.0.0.1:10013", &error), NULL);

	smlTransportSetEventCallback(client, _recv_client_event, GINT_TO_POINTER(1));

	sml_fail_unless(smlTransportInitialize(client, &error), "%s", smlErrorPrint(&error));

	SmlTransportData *data = smlTransportDataNew((char *)"test", 5, SML_MIMETYPE_XML, FALSE, &error);
	sml_fail_unless(data != NULL, NULL);
	sml_fail_unless(error == NULL, NULL);

	sml_fail_unless(smlTransportSend(client, NULL, data, &error), NULL);
	sml_fail_unless(error == NULL, NULL);

	smlTransportDataDeref(data);
		
	while (client_errors == 0) { usleep(100); };
	
	sml_fail_unless(client_connects == 0, NULL);
	sml_fail_unless(client_disconnects == 0, NULL);
	sml_fail_unless(client_receives == 0, NULL);
	sml_fail_unless(client_errors == 1, NULL);
	
	sml_fail_unless(smlTransportFinalize(client, &error), NULL);
	
	smlTransportFree(client);
}
END_TEST

START_TEST (http_reject)
{
	reset_testbed();
	
	SmlError *error = NULL;
	SmlTransport *server = smlTransportNew(SML_TRANSPORT_HTTP_SERVER, &error);
	SmlTransport *client = smlTransportNew(SML_TRANSPORT_HTTP_CLIENT, &error);
	
	sml_fail_unless(smlTransportSetConfigOption(client, "URL", "http://127.0.0.1:10014", &error), NULL);

	sml_fail_unless(smlTransportSetConfigOption(server, "PORT", "10014", &error), NULL);

	smlTransportSetEventCallback(client, _recv_client_event, GINT_TO_POINTER(1));
	smlTransportSetEventCallback(server, _recv_server_event, GINT_TO_POINTER(2));

	sml_fail_unless(smlTransportInitialize(client, &error), "%s", smlErrorPrint(&error));
	sml_fail_unless(smlTransportInitialize(server, &error), "%s", smlErrorPrint(&error));

	SmlTransportData *data = smlTransportDataNew((char *)"test", 5, SML_MIMETYPE_XML, FALSE, &error);
	sml_fail_unless(data != NULL, NULL);
	sml_fail_unless(error == NULL, NULL);

	sml_fail_unless(smlTransportSend(client, NULL, data, &error), NULL);
	sml_fail_unless(error == NULL, NULL);

	smlTransportDataDeref(data);
	
	while (server_receives == 0) { usleep(100); };
	
	sml_fail_unless(server_receives == 1, NULL);
					
	while (client_errors == 0) { usleep(100); };

	sml_fail_unless(smlTransportDisconnect(server, g_list_nth_data(links, 0), &error), NULL);
	
	while (server_disconnects == 0) { usleep(100); };

	sml_fail_unless(client_connects == 0, NULL);
	sml_fail_unless(client_disconnects == 0, NULL);
	sml_fail_unless(client_receives == 0, NULL);
	sml_fail_unless(client_errors == 1, NULL);
	sml_fail_unless(server_disconnects == 1, NULL);
	
	sml_fail_unless(smlTransportFinalize(client, &error), NULL);
	sml_fail_unless(smlTransportFinalize(server, &error), "%s", smlErrorPrint(&error));
	
	smlTransportFree(client);
	smlTransportFree(server);
}
END_TEST

START_TEST (http_multi_connect)
{
	reset_testbed();
	
	SmlError *error = NULL;
	SmlTransport *server = smlTransportNew(SML_TRANSPORT_HTTP_SERVER, &error);
	SmlTransport *client1 = smlTransportNew(SML_TRANSPORT_HTTP_CLIENT, &error);
	SmlTransport *client2 = smlTransportNew(SML_TRANSPORT_HTTP_CLIENT, &error);
	SmlTransport *client3 = smlTransportNew(SML_TRANSPORT_HTTP_CLIENT, &error);
	
	sml_fail_unless(smlTransportSetConfigOption(client1, "URL", "http://127.0.0.1:10015", &error), NULL);
	sml_fail_unless(smlTransportSetConfigOption(client2, "URL", "http://127.0.0.1:10015", &error), NULL);
	sml_fail_unless(smlTransportSetConfigOption(client3, "URL", "http://127.0.0.1:10015", &error), NULL);

	sml_fail_unless(smlTransportSetConfigOption(server, "PORT", "10015", &error), NULL);

	smlTransportSetEventCallback(client1, _recv_client_event, GINT_TO_POINTER(1));
	smlTransportSetEventCallback(client2, _recv_client_event, GINT_TO_POINTER(1));
	smlTransportSetEventCallback(client3, _recv_client_event, GINT_TO_POINTER(1));
	smlTransportSetEventCallback(server, _recv_server_event, GINT_TO_POINTER(1));

	sml_fail_unless(smlTransportInitialize(client1, &error), "%s", smlErrorPrint(&error));
	sml_fail_unless(smlTransportInitialize(client2, &error), "%s", smlErrorPrint(&error));
	sml_fail_unless(smlTransportInitialize(client3, &error), "%s", smlErrorPrint(&error));
	sml_fail_unless(smlTransportInitialize(server, &error), "%s", smlErrorPrint(&error));

	SmlTransportData *data = smlTransportDataNew((char *)"test", 5, SML_MIMETYPE_XML, FALSE, &error);
	sml_fail_unless(data != NULL, NULL);
	sml_fail_unless(error == NULL, NULL);
	sml_fail_unless(smlTransportSend(client1, NULL, data, &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	smlTransportDataDeref(data);
	
	/* It is necessary to perform the first request alone
	 * because otherwise the test can fail on Solaris
	 * because Solaris uses an outdated libsoup 2.2.
	 */
	while (client_receives != 1) {
		usleep(100);
	}
	
	data = smlTransportDataNew((char *)"test", 5, SML_MIMETYPE_XML, FALSE, &error);
	sml_fail_unless(data != NULL, NULL);
	sml_fail_unless(error == NULL, NULL);
	sml_fail_unless(smlTransportSend(client2, NULL, data, &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	smlTransportDataDeref(data);
	
	data = smlTransportDataNew((char *)"test", 5, SML_MIMETYPE_XML, FALSE, &error);
	sml_fail_unless(data != NULL, NULL);
	sml_fail_unless(error == NULL, NULL);
	sml_fail_unless(smlTransportSend(client3, NULL, data, &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	smlTransportDataDeref(data);
	
	while (client_receives != 3) {
		usleep(100);
	}
	
	sml_fail_unless(client_receives == 3, NULL);
	sml_fail_unless(server_receives == 3, NULL);

	sml_fail_unless(smlTransportDisconnect(client1, NULL, &error), "%s", smlErrorPrint(&error));
	sml_fail_unless(smlTransportDisconnect(client2, NULL, &error), "%s", smlErrorPrint(&error));
	sml_fail_unless(smlTransportDisconnect(client3, NULL, &error), "%s", smlErrorPrint(&error));
	while (client_disconnects < 3) {
		usleep(100);
	};

	int i;
	for (i = g_list_length(links); i > 0; i--) {
		/* start to disconnect from the end
		 * to avoid conflicts with very fast SMP machines
		 * which can process disconnects so fast
		 * that the list will be cleaned up before
		 * this for-loop ends
		 */
		sml_fail_unless(smlTransportDisconnect(server, g_list_nth_data(links, i-1), &error), "%s", smlErrorPrint(&error));
		while (server_disconnects < 3 - i + 1) {
			usleep(100);
		}
	}

	sml_fail_unless(client_connects == 3, NULL);
	sml_fail_unless(server_connects == 3, NULL);
	sml_fail_unless(client_disconnects == 3, NULL);
	sml_fail_unless(server_disconnects == 3, NULL);
	
	sml_fail_unless(smlTransportFinalize(server, &error), NULL);
	sml_fail_unless(smlTransportFinalize(client1, &error), NULL);
	sml_fail_unless(smlTransportFinalize(client2, &error), NULL);
	sml_fail_unless(smlTransportFinalize(client3, &error), NULL);
	
	smlTransportFree(server);
	smlTransportFree(client1);
	smlTransportFree(client2);
	smlTransportFree(client3);
}
END_TEST

START_TEST (http_multi_partial_error)
{
	reset_testbed();
	
	SmlError *error = NULL;
	SmlTransport *server = smlTransportNew(SML_TRANSPORT_HTTP_SERVER, &error);
	SmlTransport *client1 = smlTransportNew(SML_TRANSPORT_HTTP_CLIENT, &error);
	SmlTransport *client2 = smlTransportNew(SML_TRANSPORT_HTTP_CLIENT, &error);
	SmlTransport *client3 = smlTransportNew(SML_TRANSPORT_HTTP_CLIENT, &error);
	
	sml_fail_unless(smlTransportSetConfigOption(client1, "URL", "http://127.0.0.1:10016", &error), NULL);
	sml_fail_unless(smlTransportSetConfigOption(client2, "URL", "http://127.0.0.1:10016", &error), NULL);
	sml_fail_unless(smlTransportSetConfigOption(client3, "URL", "http://127.0.0.1:10016", &error), NULL);

	sml_fail_unless(smlTransportSetConfigOption(server, "PORT", "10016", &error), NULL);

	smlTransportSetEventCallback(client1, _recv_client_event, GINT_TO_POINTER(1));
	smlTransportSetEventCallback(client2, _recv_client_event, GINT_TO_POINTER(1));
	smlTransportSetEventCallback(client3, _recv_client_event, GINT_TO_POINTER(1));
	smlTransportSetEventCallback(server, _recv_server_event, GINT_TO_POINTER(1));

	sml_fail_unless(smlTransportInitialize(client1, &error), "%s", smlErrorPrint(&error));
	sml_fail_unless(smlTransportInitialize(client2, &error), "%s", smlErrorPrint(&error));
	sml_fail_unless(smlTransportInitialize(client3, &error), "%s", smlErrorPrint(&error));
	sml_fail_unless(smlTransportInitialize(server, &error), "%s", smlErrorPrint(&error));

	SmlTransportData *data = smlTransportDataNew((char *)"test", 5, SML_MIMETYPE_XML, FALSE, &error);
	sml_fail_unless(data != NULL, NULL);
	sml_fail_unless(error == NULL, NULL);
	sml_fail_unless(smlTransportSend(client1, NULL, data, &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	smlTransportDataDeref(data);
	
	/* It is necessary to perform the working request first
	 * because otherwise the test can fail on Solaris
	 * because Solaris uses an outdated libsoup 2.2.
	 */
	while (client_receives != 1) {
		usleep(100);
	}
	
	data = smlTransportDataNew((char *)"error", 6, SML_MIMETYPE_XML, FALSE, &error);
	sml_fail_unless(data != NULL, NULL);
	sml_fail_unless(error == NULL, NULL);
	sml_fail_unless(smlTransportSend(client2, NULL, data, &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	smlTransportDataDeref(data);
	
	
	data = smlTransportDataNew((char *)"error", 6, SML_MIMETYPE_XML, FALSE, &error);
	sml_fail_unless(data != NULL, NULL);
	sml_fail_unless(error == NULL, NULL);
	sml_fail_unless(smlTransportSend(client3, NULL, data, &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	smlTransportDataDeref(data);
	
	while (client_receives != 1 || client_errors != 2) {
		usleep(100);
	}
	
	sml_fail_unless(client_receives == 1, NULL);
	sml_fail_unless(client_errors == 2, NULL);
	sml_fail_unless(server_receives == 3, NULL);
	
	sml_fail_unless(smlTransportDisconnect(client1, NULL, &error), "%s", smlErrorPrint(&error));
	while (client_disconnects < 1) {
		usleep(100);
	}

	int i;
	for (i = g_list_length(links); i > 0; i--) {
		/* start to disconnect from the end
		 * to avoid conflicts with very fast SMP machines
		 * which can process disconnects so fast
		 * that the list will be cleaned up before
		 * this for-loop ends
		 */
		sml_fail_unless(smlTransportDisconnect(server, g_list_nth_data(links, i-1), &error), "%s", smlErrorPrint(&error));
		while (server_disconnects < 3 - i + 1) {
			usleep(100);
		}
	}
	
	sml_fail_unless(client_connects == 1, NULL);
	sml_fail_unless(server_connects == 3, NULL);
	sml_fail_unless(client_disconnects == 1, NULL);
	sml_fail_unless(server_disconnects == 3, NULL);
	
	sml_fail_unless(smlTransportFinalize(server, &error), NULL);
	sml_fail_unless(smlTransportFinalize(client1, &error), NULL);
	sml_fail_unless(smlTransportFinalize(client2, &error), NULL);
	sml_fail_unless(smlTransportFinalize(client3, &error), NULL);
	
	smlTransportFree(server);
	smlTransportFree(client1);
	smlTransportFree(client2);
	smlTransportFree(client3);
}
END_TEST

START_TEST (http_multi_stress)
{
	reset_testbed();
	int num = 1000;
	
	SmlError *error = NULL;
	SmlTransport *server = smlTransportNew(SML_TRANSPORT_HTTP_SERVER, &error);
	SmlTransport *client1 = smlTransportNew(SML_TRANSPORT_HTTP_CLIENT, &error);
	SmlTransport *client2 = smlTransportNew(SML_TRANSPORT_HTTP_CLIENT, &error);
	SmlTransport *client3 = smlTransportNew(SML_TRANSPORT_HTTP_CLIENT, &error);
	
	sml_fail_unless(smlTransportSetConfigOption(client1, "URL", "http://127.0.0.1:10017", &error), NULL);
	sml_fail_unless(smlTransportSetConfigOption(client2, "URL", "http://127.0.0.1:10017", &error), NULL);
	sml_fail_unless(smlTransportSetConfigOption(client3, "URL", "http://127.0.0.1:10017", &error), NULL);

	sml_fail_unless(smlTransportSetConfigOption(server, "PORT", "10017", &error), NULL);

	smlTransportSetEventCallback(client1, _recv_client_event, GINT_TO_POINTER(1));
	smlTransportSetEventCallback(client2, _recv_client_event, GINT_TO_POINTER(1));
	smlTransportSetEventCallback(client3, _recv_client_event, GINT_TO_POINTER(1));
	smlTransportSetEventCallback(server, _recv_server_event, GINT_TO_POINTER(1));

	sml_fail_unless(smlTransportInitialize(client1, &error), "%s", smlErrorPrint(&error));
	sml_fail_unless(smlTransportInitialize(client2, &error), "%s", smlErrorPrint(&error));
	sml_fail_unless(smlTransportInitialize(client3, &error), "%s", smlErrorPrint(&error));
	sml_fail_unless(smlTransportInitialize(server, &error), "%s", smlErrorPrint(&error));

	SmlTransportData *data = NULL;
	
	int i;
	for (i = 0; i < num; i++) {
		data = smlTransportDataNew((char *)"test", 5, SML_MIMETYPE_XML, FALSE, &error);
		sml_fail_unless(data != NULL, NULL);
		sml_fail_unless(error == NULL, NULL);
		sml_fail_unless(smlTransportSend(client1, NULL, data, &error), NULL);
		sml_fail_unless(error == NULL, NULL);
		smlTransportDataDeref(data);

		/* It is necessary to perform the first request alone
		 * because otherwise the test can fail on Solaris
		 * because Solaris uses an outdated libsoup 2.2.
		 */
		while (client_receives < 1) {
			usleep(10);
		}
		
		data = smlTransportDataNew((char *)"test", 5, SML_MIMETYPE_XML, FALSE, &error);
		sml_fail_unless(data != NULL, NULL);
		sml_fail_unless(error == NULL, NULL);
		sml_fail_unless(smlTransportSend(client2, NULL, data, &error), NULL);
		sml_fail_unless(error == NULL, NULL);
		smlTransportDataDeref(data);
		
		data = smlTransportDataNew((char *)"test", 5, SML_MIMETYPE_XML, FALSE, &error);
		sml_fail_unless(data != NULL, NULL);
		sml_fail_unless(error == NULL, NULL);
		sml_fail_unless(smlTransportSend(client3, NULL, data, &error), NULL);
		sml_fail_unless(error == NULL, NULL);
		smlTransportDataDeref(data);
		
		while (1) {
			usleep(1);
			if (client_receives == (i+1)*3)
				break;
		}
	}
	
	sml_fail_unless(client_receives == 3 * num, NULL);
	sml_fail_unless(server_receives == 3 * num, NULL);
	
	sml_fail_unless(smlTransportDisconnect(client1, NULL, &error), "%s", smlErrorPrint(&error));
	sml_fail_unless(smlTransportDisconnect(client2, NULL, &error), "%s", smlErrorPrint(&error));
	sml_fail_unless(smlTransportDisconnect(client3, NULL, &error), "%s", smlErrorPrint(&error));
	for (i = g_list_length(links); i > 0; i--) {
		/* start to disconnect from the end
		 * to avoid conflicts with very fast SMP machines
		 * which can process disconnects so fast
		 * that the list will be cleaned up before
		 * this for-loop ends
		 */
		sml_fail_unless(smlTransportDisconnect(server, g_list_nth_data(links, i-1), &error), "%s", smlErrorPrint(&error));
		while (server_disconnects < (3 * num - i + 1)) {
			usleep(100);
		}
	}
	
	sml_fail_unless(client_connects == 3, NULL);
	sml_fail_unless(server_connects == 3 * num, NULL);
	sml_fail_unless(client_disconnects == 3, NULL);
	sml_fail_unless(server_disconnects == 3 * num, NULL);
	
	sml_fail_unless(smlTransportFinalize(server, &error), NULL);
	sml_fail_unless(smlTransportFinalize(client1, &error), NULL);
	sml_fail_unless(smlTransportFinalize(client2, &error), NULL);
	sml_fail_unless(smlTransportFinalize(client3, &error), NULL);
	
	smlTransportFree(server);
	smlTransportFree(client1);
	smlTransportFree(client2);
	smlTransportFree(client3);
}
END_TEST

@SML_TESTCASE_CODE@

